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 源码分析笔记(2) 变量列表
May 28 Javascript
jQuery EasyUI API 中文文档 可调整尺寸
Sep 29 Javascript
javascript中createElement的两种创建方式
May 14 Javascript
JQuery中Ajax()的data参数类型实例分析
Dec 15 Javascript
高性能JavaScript循环语句和条件语句
Jan 20 Javascript
js改变style样式和css样式的简单实例
Jun 28 Javascript
AngularJS实现数据列表的增加、删除和上移下移等功能实例
Sep 05 Javascript
javascript checkbox/radio onchange不能兼容ie8处理办法
Jun 13 Javascript
jQuery Validate格式验证功能实例代码(包括重名验证)
Jul 18 jQuery
vue组件的写法汇总
Apr 12 Javascript
vue自定义指令实现方法详解
Feb 11 Javascript
基于node简单实现RSA加解密的方法步骤
Mar 21 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:风雨欲来 路在何方?
2006/10/09 PHP
配置最新的PHP加MYSQL服务器
2006/10/09 PHP
php实现首页链接查询 友情链接检查的代码
2010/01/05 PHP
Could not load type System.ServiceModel.Activation.HttpModule解决办法
2012/12/29 PHP
深入理解:XML与对象的序列化与反序列化
2013/06/08 PHP
laravel 出现command not found问题的解决方案
2019/10/23 PHP
jquery pagination插件实现无刷新分页代码
2009/10/13 Javascript
JavaScript经典效果集锦
2010/07/06 Javascript
Javascript 面向对象编程(一) 封装
2011/08/28 Javascript
调用jQuery滑出效果时闪烁的解决方法
2014/03/27 Javascript
Bootstrap 模态框(Modal)插件代码解析
2016/12/21 Javascript
jquery插件ContextMenu设置右键菜单
2017/03/13 Javascript
详解nodejs解压版安装和配置(带有搭建前端项目脚手架)
2018/12/06 NodeJs
解析JS在获取当前月的最后一天遇到的坑
2019/08/30 Javascript
JS co 函数库的含义和用法实例总结
2020/04/08 Javascript
用smtplib和email封装python发送邮件模块类分享
2014/02/17 Python
Python使用 Beanstalkd 做异步任务处理的方法
2018/04/24 Python
使用selenium模拟登录解决滑块验证问题的实现
2019/05/10 Python
Python PyPDF2模块安装使用解析
2020/01/19 Python
Matplotlib使用字符串代替变量绘制散点图的方法
2020/02/17 Python
Python实现爬取网页中动态加载的数据
2020/08/17 Python
全球度假村:Club Med
2017/11/27 全球购物
什么是Deployment descriptors;都有什么类型的部署描述符
2015/07/28 面试题
易程科技软件测试笔试
2013/03/24 面试题
幼师自荐信范文
2013/10/06 职场文书
求职简历中的自我评价分享
2013/12/08 职场文书
初中校园之声广播稿
2014/01/15 职场文书
2014年元旦活动方案
2014/02/15 职场文书
培训班开班仪式主持词
2014/03/28 职场文书
逃课打麻将检讨书
2014/10/05 职场文书
个人批评与自我批评总结
2014/10/17 职场文书
小学生2015教师节演讲稿
2015/03/19 职场文书
2015年银行信贷员工作总结
2015/05/19 职场文书
付款证明格式范文
2015/06/19 职场文书
天气温馨提示语
2015/07/14 职场文书
导游词之广西漓江
2019/11/02 职场文书