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 Change与bind事件代码
Sep 29 Javascript
多次注册事件会导致一个事件被触发多次的解决方法
Aug 12 Javascript
使用jquery+CSS实现控制打印样式
Dec 31 Javascript
详解JavaScript的Date对象(制作简易钟表)
Apr 07 Javascript
node.js中module.exports与exports用法上的区别
Sep 02 Javascript
JavaScript实现256色转灰度图
Feb 22 Javascript
Vue的Flux框架之Vuex状态管理器
Jul 30 Javascript
layui表格checkbox选择全选样式及功能的实例
Mar 07 Javascript
vue实现未登录跳转到登录页面的方法
Jul 17 Javascript
highCharts提示框中显示当前时间的方法
Jan 18 Javascript
js如何获取访问IP、地区、当前操作浏览器
Jul 23 Javascript
vuex(vue状态管理)的特殊应用案例分享
Mar 03 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仿discuz分页效果代码
2008/10/02 PHP
php与paypal整合方法
2010/11/28 PHP
浅谈PHP5.6 与 PHP7.0 区别
2019/10/09 PHP
javascript截取字符串小结
2015/04/28 Javascript
jquery实现动静态条形统计图
2015/08/17 Javascript
详解Angularjs中的依赖注入
2016/03/11 Javascript
AngularJS入门教程之AngularJS模型
2016/04/18 Javascript
深入解析Backbone.js框架的依赖库Underscore.js的作用
2016/05/07 Javascript
jQuery生成假加载动画效果
2016/12/01 Javascript
AngularJS的依赖注入实例分析(使用module和injector)
2017/01/19 Javascript
原生JS轮播图插件
2017/02/09 Javascript
使用canvas及js简单生成验证码方法
2017/04/02 Javascript
详解vue项目中调用百度地图API使用方法
2019/04/25 Javascript
Vue.js 无限滚动列表性能优化方案
2019/12/02 Javascript
[56:18]DOTA2上海特级锦标赛主赛事日 - 4 败者组第四轮#2 MVP.Phx VS Fnatic第二局
2016/03/05 DOTA
[02:59]DOTA2完美大师赛主赛事第三日精彩集锦
2017/11/25 DOTA
python正常时间和unix时间戳相互转换的方法
2015/04/23 Python
python简单实现旋转图片的方法
2015/05/30 Python
Python连接数据库学习之DB-API详解
2017/02/07 Python
Python实现将照片变成卡通图片的方法【基于opencv】
2018/01/17 Python
Python 错误和异常代码详解
2018/01/29 Python
python中返回矩阵的行列方法
2018/04/04 Python
python输出100以内的质数与合数实例代码
2018/07/08 Python
详解PyTorch手写数字识别(MNIST数据集)
2019/08/16 Python
基于python实现雪花算法过程详解
2019/11/16 Python
python实现滑雪者小游戏
2020/02/22 Python
如何在Python 游戏中模拟引力
2020/03/27 Python
python:删除离群值操作(每一行为一类数据)
2020/06/08 Python
事业单位接收函
2014/01/10 职场文书
2014年初中班主任工作总结
2014/11/08 职场文书
运动会闭幕式主持词
2015/07/01 职场文书
2019求职信:应届生求职信范文
2019/04/24 职场文书
毕业生自荐求职信书写的技巧
2019/08/26 职场文书
python实现大文本文件分割成多个小文件
2021/04/20 Python
Python socket如何解析HTTP请求内容
2022/02/12 Python
PyTorch device与cuda.device用法
2022/04/03 Python