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 特殊字符
Apr 05 Javascript
IE与FireFox的兼容性问题分析
Apr 22 Javascript
jQuery autocomplete插件修改
Apr 17 Javascript
JS获取页面窗口大小的代码解读
Dec 01 Javascript
THREE.JS入门教程(4)创建粒子系统
Jan 24 Javascript
早该知道的7个JavaScript技巧
Mar 27 Javascript
探讨jQuery的ajax使用场景(c#)
Dec 03 Javascript
JavaScript数据结构和算法之二叉树详解
Feb 11 Javascript
jquery正则表达式验证(手机号、身份证号、中文名称)
Dec 31 Javascript
通过Tabs方法基于easyUI+bootstrap制作工作站
Mar 28 Javascript
mvc中form表单提交的三种方式(推荐)
Aug 10 Javascript
Bootstrap导航条鼠标悬停下拉菜单
Jan 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
收音机鉴频器对声音的影响和频偏分析
2021/03/02 无线电
PHP使用imagick扩展实现合并图像的方法
2017/04/25 PHP
php操作redis数据库常见方法实例总结
2020/02/20 PHP
PhpStorm连接服务器并实现自动上传功能
2020/12/09 PHP
js 小贴士一星期合集
2010/04/07 Javascript
jQuery图片预加载 等比缩放实现代码
2011/10/04 Javascript
Javascript 按位左移运算符使用介绍(
2014/02/04 Javascript
js简单实现竖向tab选项卡的方法
2015/05/04 Javascript
jQuery解决$符号命名冲突
2016/06/18 Javascript
[原创]jQuery常用的4种加载方式分析
2016/07/25 Javascript
jQuery弹出下拉列表插件(实现kindeditor的@功能)
2016/08/16 Javascript
JS中的函数与对象的创建方式
2019/05/12 Javascript
jQuery Ajax async=&gt;false异步改为同步时,解决导致浏览器假死的问题
2019/07/22 jQuery
Python操作CouchDB数据库简单示例
2015/03/10 Python
python实现定时播放mp3
2015/03/29 Python
python清除字符串里非数字字符的方法
2015/07/02 Python
Python Django使用forms来实现评论功能
2016/08/17 Python
python利用rsa库做公钥解密的方法教程
2017/12/10 Python
分析运行中的 Python 进程详细解析
2019/06/22 Python
python单线程下实现多个socket并发过程详解
2019/07/27 Python
keras 特征图可视化实例(中间层)
2020/01/24 Python
python读取csv文件指定行的2种方法详解
2020/02/13 Python
Python实现自动签到脚本功能
2020/08/20 Python
如何使用python socket模块实现简单的文件下载
2020/09/04 Python
python中doctest库实例用法
2020/12/31 Python
马德里竞技官方网上商店:Atletico Madrid Shop
2019/03/31 全球购物
介绍java中初始化块的使用
2012/09/11 面试题
英文版银行求职信
2013/10/09 职场文书
应聘美工求职信
2013/11/07 职场文书
网上蛋糕店创业计划书
2014/01/24 职场文书
中国入世承诺
2014/04/01 职场文书
2014年学生党支部工作总结
2014/12/20 职场文书
慰问信模板
2015/02/14 职场文书
公司禁烟通知
2015/04/23 职场文书
2016教师学习教育法心得体会
2016/01/19 职场文书
Java的Object类的九种方法
2022/04/13 Java/Android