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结合html5 canvas实现(可调画笔颜色/粗细/橡皮)的涂鸦板
Apr 27 Javascript
node.js中的fs.truncate方法使用说明
Dec 15 Javascript
jQuery选择器querySelector的使用指南
Jan 23 Javascript
jquery中val()方法是从最后一个选项往前读取的
Sep 06 Javascript
EasyUI布局 高度自适应
Jun 04 Javascript
js中删除数组中的某一元素实例(无下标时)
Feb 28 Javascript
angularjs实现柱状图动态加载的示例
Dec 11 Javascript
Vue.js上传图片到阿里云OSS存储的方法示例
Dec 13 Javascript
vue单文件组件lint error自动fix与styleLint报错自动fix详解
Jan 08 Javascript
Vue.js项目实战之多语种网站的功能实现(租车)
Aug 07 Javascript
微信小程序定义和调用全局变量globalData的实现
Nov 01 Javascript
详解vue页面首次加载缓慢原因及解决方案
Nov 06 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添加MySQL数据记录代码
2008/06/07 PHP
php判断输入不超过mysql的varchar字段的长度范围
2011/06/24 PHP
浅析十款PHP开发框架的对比
2013/07/05 PHP
php动态变量定义及使用
2015/06/10 PHP
THINKPHP5.1 Config的配置与获取详解
2020/06/08 PHP
jquery 操作DOM的基本用法分享
2012/04/05 Javascript
jquery 关于event.target使用的几点说明介绍
2013/04/26 Javascript
node.js中的console.trace方法使用说明
2014/12/09 Javascript
JQuery通过AJAX从后台获取信息显示在表格上并支持行选中
2015/09/15 Javascript
JavaScript如何调试有哪些建议和技巧附五款有用的调试工具
2015/10/28 Javascript
JavaScript代码性能优化总结(推荐)
2016/05/16 Javascript
JavaScript数组合并的多种方法
2016/05/22 Javascript
响应式表格之固定表头的简单实现
2016/08/26 Javascript
彻底解决 webpack 打包文件体积过大问题
2017/07/07 Javascript
让nodeJS支持ES6的词法----babel的安装和使用方法
2017/07/31 NodeJs
JavaScript中in和hasOwnProperty区别详解
2017/08/04 Javascript
javascript基本常用排序算法解析
2017/09/27 Javascript
原生js检测页面加载完毕的实例
2018/09/11 Javascript
原生js无缝轮播插件使用详解
2020/03/09 Javascript
JS co 函数库的含义和用法实例总结
2020/04/08 Javascript
详解用js代码触发dom事件的实现方案
2020/06/10 Javascript
python实现socket客户端和服务端简单示例
2014/02/24 Python
python实现类的静态变量用法实例
2015/05/08 Python
Python常用算法学习基础教程
2017/04/13 Python
python 利用浏览器 Cookie 模拟登录的用户访问知乎的方法
2019/07/11 Python
python中使用while循环的实例
2019/08/05 Python
tensorflow 实现自定义layer并添加到计算图中
2020/02/04 Python
Tensorflow 1.0之后模型文件、权重数值的读取方式
2020/02/12 Python
深入浅析Python 函数注解与匿名函数
2020/02/24 Python
Python监听键盘和鼠标事件的示例代码
2020/11/18 Python
美国玛丽莎收藏奢华时尚商店:Marissa Collections
2016/11/21 全球购物
Theflamel意大利:女士奢华服装、鞋子和配件
2020/01/11 全球购物
当当网软件测试笔试题
2015/11/24 面试题
工程师求职简历的自我评价分享
2013/10/10 职场文书
DSP接收机前端设想
2022/04/05 无线电
3050和2060哪个好 性能差多少 差距有多大 谁更有性价比
2022/06/17 数码科技