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 禁止复制网页
Jun 11 Javascript
JavaScript去掉空格的方法集合
Dec 28 Javascript
JS实现仿QQ面板的手风琴效果折叠菜单代码
Sep 11 Javascript
jQuery图片轮播滚动切换代码分享
Apr 20 Javascript
jQuery实现仿QQ在线客服效果的滚动层代码
Oct 15 Javascript
jQuery ajax应用总结
Jun 02 Javascript
js enter键激发事件实例代码
Aug 17 Javascript
JS表单提交验证、input(type=number) 去三角 刷新验证码
Jun 21 Javascript
Vue from-validate 表单验证的示例代码
Sep 26 Javascript
angular4笔记系列之内置指令小结
Nov 09 Javascript
JavaScript switch语句使用方法简介
Dec 30 Javascript
JS常用排序方法实例代码解析
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
无线电的诞生过程
2021/03/01 无线电
ThinkPHP之R方法实例详解
2014/06/20 PHP
Asp.net下使用Jquery Ajax传送和接收DataTable的代码
2010/09/12 Javascript
JQuery与JSon实现的无刷新分页代码
2011/09/13 Javascript
解析jQuery与其它js(Prototype)库兼容共存
2013/07/04 Javascript
使用JS实现jQuery的addClass, removeClass, hasClass函数功能
2014/10/31 Javascript
基于jquery实现在线选座订座之影院篇
2015/08/24 Javascript
javascript从作用域链谈闭包
2020/07/29 Javascript
jQuery 跨域访问解决原理案例详解
2016/07/09 Javascript
JavaScript中自带的 reduce()方法使用示例详解
2016/08/10 Javascript
通过原生JS实现为元素添加事件的方法
2016/11/23 Javascript
JS实现控制图片显示大小的方法【图片等比例缩放功能】
2017/02/18 Javascript
bootstrap-table组合表头的实现方法
2017/09/07 Javascript
javascript中的replace函数(带注释demo)
2018/01/07 Javascript
JS简单实现查看文档创建日期、修改日期和文档大小的方法示例
2018/04/08 Javascript
JavaScript之数组扁平化详解
2019/06/03 Javascript
Vue代码整洁之去重方法整理
2019/08/06 Javascript
[56:17]NB vs Infamous 2019国际邀请赛淘汰赛 败者组 BO3 第三场 8.22
2019/09/05 DOTA
python之文件的读写和文件目录以及文件夹的操作实现代码
2016/08/28 Python
django中模板的html自动转意方法
2018/05/27 Python
详解关于Django中ORM数据库迁移的配置
2018/10/08 Python
通过python实现弹窗广告拦截过程详解
2019/07/10 Python
在Pytorch中使用Mask R-CNN进行实例分割操作
2020/06/24 Python
MoviePy常用剪辑类及Python视频剪辑自动化
2020/12/18 Python
VSCode中autopep8无法运行问题解决方案(提示Error: Command failed,usage)
2021/03/02 Python
纯DOM+CSS3实现简单的小风车动画
2016/09/27 HTML / CSS
用纯css3和html制作泡沫对话框实现代码
2013/03/21 HTML / CSS
德国机车企业:FC-Moto
2017/10/27 全球购物
西班牙美妆电商:Perfume’s Club(有中文站)
2018/08/08 全球购物
中国文明网签名寄语
2014/01/18 职场文书
公司股东出资证明书
2014/11/01 职场文书
2015年世界无烟日活动总结
2015/02/10 职场文书
用Python写一个简易版弹球游戏
2021/04/13 Python
opencv 分类白天与夜景视频的方法
2021/06/05 Python
浅谈Go语言多态的实现与interface使用
2021/06/16 Golang
Win10开机修复磁盘错误怎么跳过?Win10关闭开机磁盘检查的方法
2022/09/23 数码科技