vue源码学习之Object.defineProperty 对数组监听


Posted in Javascript onMay 30, 2018

上一篇中,我们介绍了一下defineProperty 对对象的监听,这一篇我们看下defineProperty 对数组的监听

数组的变化

先让我们了解下Object.defineProperty()对数组变化的跟踪情况:

var a={};
bValue=1;
Object.defineProperty(a,"b",{
  set:function(value){
    bValue=value;
    console.log("setted");
  },
  get:function(){
    return bValue;
  }
});
a.b;//1
a.b=[];//setted
a.b=[1,2,3];//setted
a.b[1]=10;//无输出
a.b.push(4);//无输出
a.b.length=5;//无输出
a.b;//[1,10,3,4,undefined];

可以看到,当a.b被设置为数组后,只要不是重新赋值一个新的数组对象,任何对数组内部的修改都不会触发setter方法的执行。这一点非常重要,因为基于Object.defineProperty()方法的现代前端框架实现的数据双向绑定也同样无法识别这样的数组变化。因此第一点,如果想要触发数据双向绑定,我们不要使用arr[1]=newValue;这样的语句来实现;第二点,框架也提供了许多方法来实现数组的双向绑定。

对于框架如何实现数组变化的监测,大多数情况下,框架会重写Array.prototype.push方法,并生成一个新的数组赋值给数据,这样数据双向绑定就会触发。

实现简单的对数组的变化的监听

var arrayPush = {};
(function(method){
  var original = Array.prototype[method];
  arrayPush[method] = function() {
    // this 指向可通过下面的测试看出
    console.log(this);
    return original.apply(this, arguments)
  };
})('push');

var testPush = [];
testPush.__proto__ = arrayPush;
// 通过输出,可以看出上面所述 this 指向的是 testPush
// []
testPush.push(1);
// [1]
testPush.push(2);

在官方文档,所需监视的只有 push()、pop()、shift()、unshift()、splice()、sort()、reverse() 7 种方法。我们可以遍历一下:

var arrayProto = Array.prototype
var arrayMethods = Object.create(arrayProto)

;[
 'push',
 'pop',
 'shift',
 'unshift',
 'splice',
 'sort',
 'reverse'
].forEach(function(item){
  Object.defineProperty(arrayMethods,item,{
    value:function mutator(){
      //缓存原生方法,之后调用
      console.log('array被访问');
      var original = arrayProto[item]  
      var args = Array.from(arguments)
    original.apply(this,args)
      // console.log(this);
    },
  })
})

完整代码

function Observer(data){
  this.data = data;
  this.walk(data);
}

var p = Observer.prototype;
var arrayProto = Array.prototype
var arrayMethods = Object.create(arrayProto)

;[
 'push',
 'pop',
 'shift',
 'unshift',
 'splice',
 'sort',
 'reverse'
].forEach(function(item){
  Object.defineProperty(arrayMethods,item,{
    value:function mutator(){
      //缓存原生方法,之后调用
      console.log('array被访问');
      var original = arrayProto[item]  
      var args = Array.from(arguments)
    original.apply(this,args)
      // console.log(this);
    },
  })
})

p.walk = function(obj){
  var value;
  for(var key in obj){
    // 通过 hasOwnProperty 过滤掉一个对象本身拥有的属性 
    if(obj.hasOwnProperty(key)){
      value = obj[key];
      // 递归调用 循环所有对象出来
      if(typeof value === 'object'){
        if (Array.isArray(value)) {
          var augment = value.__proto__ ? protoAugment : copyAugment 
          augment(value, arrayMethods, key)
          observeArray(value)
        }
        new Observer(value);
      }
      this.convert(key, value);
    }
  }
};

p.convert = function(key, value){
  Object.defineProperty(this.data, key, {
    enumerable: true,
    configurable: true,
    get: function(){
      console.log(key + '被访问');
      return value;
    },
    set: function(newVal){
      console.log(key + '被修改,新' + key + '=' + newVal);
      if(newVal === value) return ;
      value = newVal;
    }
  })
}; 

var data = {
  user: {
    // name: 'zhangsan',
    age: function(){console.log(1)}
  },
  apg: [{'a': 'b'},2,3]
}

function observeArray (items) {
  for (var i = 0, l = items.length; i < l; i++) {
    observe(items[i])
  }
}

//数据重复Observer
function observe(value){
  if(typeof(value) != 'object' ) return;
  var ob = new Observer(value)
   return ob;
}

//辅助方法
function def (obj, key, val) {
 Object.defineProperty(obj, key, {
  value: val,
  enumerable: true,
  writable: true,
  configurable: true
 })
}

// 兼容不支持__proto__的方法
//重新赋值Array的__proto__属性
function protoAugment (target,src) {
 target.__proto__ = src
}
//不支持__proto__的直接修改相关属性方法
function copyAugment (target, src, keys) {
 for (var i = 0, l = keys.length; i < l; i++) {
  var key = keys[i]
  def(target, key, src[key])
 }
}

var app = new Observer(data);
// data.apg[2] = 111;
data.apg.push(5);
// data.apg[0].a = 10;
// console.log(data.apg);

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
ASP.NET jQuery 实例6 (实现CheckBoxList成员全选或全取消)
Jan 13 Javascript
Javascript 鼠标移动上去小三角形滑块缓慢跟随效果
Apr 26 Javascript
逐一介绍Jquery data()、Jquery stop()、jquery delay()函数(详)
Nov 04 Javascript
JavaScript简单获取页面图片原始尺寸的方法
Jun 21 Javascript
Node.js的文件权限及读写flag详解
Oct 11 Javascript
Jquery根据浏览器窗口改变调整大小的方法
Feb 07 Javascript
babel基本使用详解
Feb 17 Javascript
JS中实现函数return多个返回值的实例
Feb 21 Javascript
Node.js实现文件上传的示例
Jun 28 Javascript
基于Vue.js 2.0实现百度搜索框效果
Dec 28 Javascript
vue后台管理之动态加载路由的方法
Aug 13 Javascript
原生JS实现烟花效果
Mar 10 Javascript
vue源码学习之Object.defineProperty对象属性监听
May 30 #Javascript
Angular搜索场景中使用rxjs的操作符处理思路
May 30 #Javascript
微信小程序实现跑马灯效果完整代码(附效果图)
May 30 #Javascript
vue通过点击事件读取音频文件的方法
May 30 #Javascript
vue 表单输入格式化中文输入法异常问题
May 30 #Javascript
详解如何使用babel进行es6文件的编译
May 29 #Javascript
基于打包工具Webpack进行项目开发实例
May 29 #Javascript
You might like
又一个php 分页类实现代码
2009/12/03 PHP
Zend Studio 实用快捷键一览表(精心整理)
2013/08/10 PHP
分享一个超好用的php header下载函数
2014/01/31 PHP
PHP错误WARNING: SESSION_START() [FUNCTION.SESSION-START]解决方法
2014/05/04 PHP
详谈PHP编码转换问题
2015/07/28 PHP
php数据访问之查询关键字
2016/05/09 PHP
php微信开发之关键词回复功能
2018/06/13 PHP
functional继承模式 摘自javascript:the good parts
2011/06/20 Javascript
cument.execCommand()用法深入理解
2012/12/04 Javascript
javascript对下拉列表框(select)的操作实例讲解
2013/11/29 Javascript
javascript记住用户名和登录密码(两种方式)
2015/08/04 Javascript
使用base64对图片的二进制进行编码并用ajax进行显示
2017/01/03 Javascript
vue2笔记 — vue-router路由懒加载的实现
2017/03/03 Javascript
js使用i18n实现页面国际化的方法
2017/05/09 Javascript
基于vue中css预加载使用sass的配置方式详解
2018/03/13 Javascript
vue实现点击当前标签高亮效果【推荐】
2018/06/22 Javascript
vue+mousemove实现鼠标拖动功能(拖动过快失效问题解决方法)
2018/08/24 Javascript
Vue源码中要const _toStr = Object.prototype.toString的原因分析
2018/12/09 Javascript
浅谈JavaScript 代码简洁之道
2019/01/09 Javascript
nodejs提示:cross-device link not permitted, rename错误的解决方法
2019/06/10 NodeJs
vue响应式更新机制及不使用框架实现简单的数据双向绑定问题
2019/06/27 Javascript
vue.js实现图书管理功能
2019/09/24 Javascript
jquery实现简易验证插件封装
2020/09/13 jQuery
vue 单页应用和多页应用的优劣
2020/10/22 Javascript
python写入并获取剪切板内容的实例
2018/05/31 Python
python+opencv实现阈值分割
2018/12/26 Python
python中的subprocess.Popen()使用详解
2019/12/25 Python
解决TensorFlow程序无限制占用GPU的方法
2020/06/30 Python
tensorflow与numpy的版本兼容性问题的解决
2021/01/08 Python
亚洲颇具影响力的男性在线购物零售商:His
2019/11/24 全球购物
批评与自我批评范文
2014/10/15 职场文书
个人年终总结开头
2015/03/06 职场文书
聋哑人盗窃罪辩护词
2015/05/21 职场文书
详解Nginx 工作原理
2021/03/31 Servers
Java中使用Filter过滤器的方法
2021/06/28 Java/Android
iSCSI服务器CHAP双向认证配置
2022/04/01 Servers