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 相关文章推荐
javascript Ext JS 状态默认存储时间
Feb 15 Javascript
关于取不到由location.href提交而来的上级页面地址的解决办法
Jul 30 Javascript
javascript之querySelector和querySelectorAll使用说明
Oct 09 Javascript
jQuery 开发者应该注意的9个错误
May 03 Javascript
JavaScript通过select动态更换图片的方法
Mar 23 Javascript
jQuery中prepend()方法使用详解
Aug 11 Javascript
超链接怎么正确调用javascript函数
May 23 Javascript
JS实现简易刻度时钟示例代码
Mar 11 Javascript
vue页面跳转后返回原页面初始位置方法
Feb 11 Javascript
Node.js中的child_process模块详解
Jun 08 Javascript
详解ng-alain动态表单SF表单项设置必填和正则校验
Jun 11 Javascript
vantUI 获得piker选中值的自定义ID操作
Nov 04 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
全文搜索和替换
2006/10/09 PHP
php的webservice的wsdl的XML无法显示问题的解决方法
2014/03/11 PHP
php调用KyotoTycoon简单实例
2015/04/02 PHP
PHP会话处理的10个函数
2015/08/11 PHP
thinkphp实现分页显示功能
2016/12/03 PHP
php实现数组纵向转横向并过滤重复值的方法分析
2017/05/29 PHP
jquery 上下滚动广告
2009/06/17 Javascript
JS Replace()的高级使用方法介绍
2013/06/29 Javascript
JS获取select-option-text_value的方法
2013/12/26 Javascript
JavaScript中数组的合并以及排序实现示例
2015/10/24 Javascript
JS实战篇之收缩菜单表单布局
2016/12/10 Javascript
微信小程序内拖动图片实现移动、放大、旋转的方法
2018/09/04 Javascript
JS实现求5的阶乘示例
2019/01/21 Javascript
[06:01]刀塔次级联赛top10第一期
2014/11/07 DOTA
python中使用百度音乐搜索的api下载指定歌曲的lrc歌词
2014/07/18 Python
Python中模拟enum枚举类型的5种方法分享
2014/11/22 Python
操作Windows注册表的简单的Python程序制作教程
2015/04/07 Python
Python实现求数列和的方法示例
2018/01/12 Python
Python实现简单遗传算法(SGA)
2018/01/29 Python
python实现二维插值的三维显示
2018/12/17 Python
python 发送和接收ActiveMQ消息的实例
2019/01/30 Python
python3.4 将16进制转成字符串的实例
2019/06/12 Python
python发送多人邮件没有展示收件人问题的解决方法
2019/06/21 Python
维多利亚的秘密官方网站:Victoria’s Secret
2018/10/24 全球购物
个人求职简历的自我评价范文
2013/10/09 职场文书
中学生在校期间的自我评价分享
2013/11/13 职场文书
实习自我评价怎么写
2013/12/02 职场文书
写给学生的新学期寄语
2014/01/18 职场文书
合同意向书范本
2014/07/30 职场文书
小学生五年级大队长竞选发言稿
2014/09/12 职场文书
私营公司诉讼代理委托书范本
2014/09/13 职场文书
2015年女职工工作总结
2015/05/15 职场文书
余世维讲座观后感
2015/06/11 职场文书
2016年学校十一国庆节活动总结
2016/04/01 职场文书
2019年暑期安全广播稿!
2019/07/03 职场文书
Python 把两层列表展开平铺成一层(5种实现方式)
2021/04/07 Python