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 相关文章推荐
jqPlot jquery的页面图表绘制工具
Jul 25 Javascript
JQuery操作三大控件(下拉,单选,复选)的方法
Aug 06 Javascript
jQuery 复合选择器应用的几个例子
Sep 11 Javascript
asp.net中oracle 存储过程(图文)
Aug 12 Javascript
javascript创建cookie、读取cookie
Mar 31 Javascript
HTML中使背景图片自适应浏览器大小实例详解
Apr 06 Javascript
JavaScript设计模式之代理模式详解
Jun 09 Javascript
JS实现的数组去除重复数据算法小结
Nov 17 Javascript
对angularjs框架下controller间的传值方法详解
Oct 08 Javascript
vue 实现websocket发送消息并实时接收消息
Dec 09 Javascript
详解javascript void(0)
Jul 13 Javascript
Javascript数组及类数组相关原理详解
Oct 29 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
Ajax+PHP 边学边练之四 表单
2009/11/27 PHP
Drupal简体中文语言包安装教程
2014/09/27 PHP
分享五个PHP7性能优化提升技巧
2015/12/07 PHP
PHP实现将上传图片自动缩放到指定分辨率,并保持清晰度封装类示例
2019/06/17 PHP
thinkPHP5使用Rabc实现权限管理
2019/08/28 PHP
js文字滚动停顿效果代码
2008/06/28 Javascript
JavaScript国旗变换效果代码
2008/08/13 Javascript
在模板页面的js使用办法
2010/04/01 Javascript
javascript实例--教你实现扑克牌洗牌功能
2014/05/15 Javascript
《JavaScript DOM 编程艺术》读书笔记之JavaScript 图片库
2015/01/09 Javascript
Javascript 是你的高阶函数(高级应用)
2015/06/15 Javascript
javascript插件开发的一些感想和心得
2016/02/28 Javascript
纯JS代码实现一键分享功能
2016/04/20 Javascript
jQuery如何解决IE输入框不能输入的问题
2016/10/08 Javascript
js中通过getElementsByName访问name集合对象的方法
2016/10/31 Javascript
12条写出高质量JS代码的方法
2018/01/07 Javascript
微信小程序左滑删除功能开发案例详解
2018/11/12 Javascript
NodeJs crypto加密制作token的实现代码
2019/11/15 NodeJs
微信小程序wxs实现吸顶效果
2020/01/08 Javascript
[05:53]敌法师的金色冠名ID"BurNIng",是传说,是荣耀
2020/07/11 DOTA
安装2019Pycharm最新版本的教程详解
2019/10/22 Python
基于python3生成标签云代码解析
2020/02/18 Python
python模拟实现分发扑克牌
2020/04/22 Python
Python利用pip安装tar.gz格式的离线资源包
2020/09/14 Python
python实现银行账户系统
2021/02/22 Python
纯CSS3实现鼠标悬停提示气泡效果
2014/02/28 HTML / CSS
英国水族馆和池塘用品购物网站:Warehouse Aquatics
2019/08/29 全球购物
汽修专业学生自我鉴定
2013/11/16 职场文书
后勤采购员岗位职责
2013/12/19 职场文书
优秀高中生事迹材料
2014/02/11 职场文书
小学生环保演讲稿
2014/04/25 职场文书
党员公开承诺书内容
2014/05/20 职场文书
个人自荐材料
2014/05/23 职场文书
夫妻分居协议书范本
2014/11/28 职场文书
《飘》英文读后感五篇
2019/10/11 职场文书
golang生成vcf通讯录格式文件详情
2022/03/25 Golang