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中的height innerHeight outerHeight区别示例介绍
Jun 15 Javascript
推荐一个自己用的封装好的javascript插件
Jan 29 Javascript
es6的数字处理的方法(5个)
Mar 16 Javascript
微信小程序网络请求的封装与填坑之路
Apr 01 Javascript
详解react-router4 异步加载路由两种方法
Sep 12 Javascript
基于jquery trigger函数无法触发a标签的两种解决方法
Jan 06 jQuery
一步步教你利用webpack如何搭一个vue脚手架(超详细讲解和注释)
Jan 08 Javascript
vue实现同一个页面可以有多个router-view的方法
Sep 20 Javascript
npm scripts 使用指南详解
Oct 08 Javascript
vue实现的双向数据绑定操作示例
Dec 04 Javascript
js 根据对象数组中的属性进行排序实现代码
Sep 12 Javascript
Layui实现数据表格默认全部显示(不要分页)
Oct 26 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
一些操作和快捷键的理解和讨论
2020/03/04 星际争霸
php is_file()和is_dir()用于遍历目录时用法注意事项
2010/03/02 PHP
php数据结构与算法(PHP描述) 快速排序 quick sort
2012/06/21 PHP
php实现文件上传基本验证
2020/03/04 PHP
Nginx+php配置文件及原理解析
2020/12/09 PHP
使用jQuery模板来展现json数据的代码
2010/10/22 Javascript
jQuery自动添加表单项的方法
2015/07/13 Javascript
深入浅析javascript立即执行函数
2015/10/23 Javascript
轻松实现jquery手风琴效果
2016/01/14 Javascript
AngularJS页面访问时出现页面闪烁问题的解决
2016/03/06 Javascript
JavaScript数据结构之广义表的定义与表示方法详解
2017/04/12 Javascript
WebSocket实现简单客服聊天系统
2017/05/12 Javascript
Mac系统下Webstorm快捷键整理大全
2017/05/28 Javascript
package.json文件配置详解
2017/06/15 Javascript
实现图片首尾平滑轮播(JS原生方法—节流)
2017/10/17 Javascript
react-router browserHistory刷新页面404问题解决方法
2017/12/29 Javascript
jQuery中ajax获取数据赋值给页面的实例
2017/12/31 jQuery
vue+canvas实现炫酷时钟效果的倒计时插件(已发布到npm的vue2插件,开箱即用)
2018/11/05 Javascript
[06:53]2018DOTA2国际邀请赛寻真——为复仇而来的Newbee
2018/08/15 DOTA
21行Python代码实现拼写检查器
2016/01/25 Python
python 迭代器和iter()函数详解及实例
2017/03/21 Python
利用python微信库itchat实现微信自动回复功能
2017/05/18 Python
Python学习思维导图(必看篇)
2017/06/26 Python
在Python 中实现图片加框和加字的方法
2019/01/26 Python
Django自带的加密算法及加密模块详解
2019/12/03 Python
Python内置方法实现字符串的秘钥加解密(推荐)
2019/12/09 Python
Python文件操作及内置函数flush原理解析
2020/10/13 Python
html5 Web SQL Database 之事务处理函数transaction与executeSQL解析
2013/11/07 HTML / CSS
Gtech官方网站:地毯清洁器、吸尘器及园艺设备
2018/05/23 全球购物
企事业单位求职者的自我评价
2013/12/28 职场文书
学校三八妇女节活动情况总结
2014/03/09 职场文书
测控技术自荐信
2014/06/05 职场文书
铅球加油稿100字
2014/09/26 职场文书
给老婆的检讨书(搞笑版)
2015/05/06 职场文书
Django实现聊天机器人
2021/05/31 Python
使用pandas生成/读取csv文件的方法实例
2021/07/09 Python