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 相关文章推荐
经典海量jQuery插件 大家可以收藏一下
Feb 07 Javascript
jquery cookie实现的简单换肤功能适合小网站
Aug 25 Javascript
escape函数解决js中ajax传递中文出现乱码问题
Oct 30 Javascript
jquery插件tytabs.jquery.min.js实现渐变TAB选项卡效果
Aug 25 Javascript
jquery trigger实现联动的方法
Feb 29 Javascript
jQuery实现页面下拉100像素出现悬浮窗口的方法
Sep 05 Javascript
移动端界面的适配
Jan 11 Javascript
微信小程序实现分享到朋友圈功能
Jul 19 Javascript
深入理解JavaScript的async/await
Aug 05 Javascript
记录一篇关于redux-saga的基本使用过程
Aug 18 Javascript
详解JavaScript作用域和作用域链
Mar 19 Javascript
Element实现表格嵌套、多个表格共用一个表头的方法
May 09 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中require和include路径问题详解
2014/12/25 PHP
yii2项目实战之restful api授权验证详解
2017/05/20 PHP
javascript实现仿银行密码输入框效果的代码
2007/12/13 Javascript
一些相见恨晚的 JavaScript 技巧
2010/04/25 Javascript
基于jQuery的仿flash的广告轮播代码
2010/11/04 Javascript
密码框显示提示文字jquery示例
2013/08/29 Javascript
Jquery焦点与失去焦点示例应用
2014/06/10 Javascript
Javascript的&amp;&amp;和||的另类用法
2014/07/23 Javascript
jQuery中height()方法用法实例
2014/12/24 Javascript
js实现简单随机抽奖的方法
2015/01/27 Javascript
jQuery结合CSS制作动态的下拉菜单
2015/10/27 Javascript
创建自己的jquery表格插件
2015/11/25 Javascript
JavaScript 实现的 zip 压缩和解压缩工具包Zip.js使用详解
2015/12/14 Javascript
详解Javascript事件驱动编程
2016/01/03 Javascript
JS 实现可停顿的垂直滚动实例代码
2016/11/23 Javascript
基于javascript实现数字英文验证码
2017/01/25 Javascript
jquery.uploadView 实现图片预览上传功能
2017/08/10 jQuery
js禁止表单重复提交
2017/08/29 Javascript
Vue中v-show添加表达式的问题(判断是否显示)
2018/03/26 Javascript
快速对接payjq的个人微信支付接口过程解析
2019/08/15 Javascript
vue中使用v-for时为什么不能用index作为key
2020/04/04 Javascript
Python中模拟enum枚举类型的5种方法分享
2014/11/22 Python
Python远程桌面协议RDPY安装使用介绍
2015/04/15 Python
Python中使用支持向量机(SVM)算法
2017/12/26 Python
python控制windows剪贴板,向剪贴板中写入图片的实例
2018/05/31 Python
对python3 一组数值的归一化处理方法详解
2018/07/11 Python
Python实现全排列的打印
2018/08/18 Python
使用Python进行目录的对比方法
2018/11/01 Python
Python键鼠操作自动化库PyAutoGUI简介(小结)
2020/05/17 Python
CSS3实现水平居中、垂直居中、水平垂直居中的实例代码
2020/02/27 HTML / CSS
css3 transform 3d 使用css3创建动态3d立方体(html5实践)
2013/01/06 HTML / CSS
3种方式实现瀑布流布局小结
2019/09/05 HTML / CSS
Staples美国官方网站:办公用品一站式采购
2016/07/28 全球购物
教师党员个人整改措施
2014/10/27 职场文书
关于践行三严三实的心得体会
2016/01/05 职场文书
浅谈Mysql多表连接查询的执行细节
2021/04/24 MySQL