上一篇中,我们介绍了一下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);

vue源码学习:Object.defineProperty 对数组监听相关推荐

  1. Vue源码学习之Computed与Watcher原理

    前言  computed与watch是我们在vue中常用的操作,computed是一个惰性求值观察者,具有缓存性,只有当依赖发生变化,第一次访问computed属性,才会计算新的值.而watch则是当 ...

  2. Vue源码学习 - 准备工作

    Vue源码学习 - 准备工作 准备工作 认识Flow 为什么用 Flow Flow 的工作方式 类型推断 类型注释 数组 类和对象 null Flow 在 Vue.js 源码中的应用 flow实践 总 ...

  3. vue源码学习--vue源码学习入门

    本文为开始学习vue源码的思路整理.在拿到vue项目源码的之后看到那些项目中的文件夹,会很困惑,不知道每个文件夹内的世界,怎么变换,怎样的魔力,最后产生了vue框架.学习源码也无从学起.我解决了这些困 ...

  4. Vue源码学习 - 组件化(三) 合并配置

    Vue源码学习 - 组件化(三) 合并配置 合并配置 外部调用场景 组件场景 总结 学习内容和文章内容来自 黄轶老师 黄轶老师的慕课网视频教程地址:<Vue.js2.0 源码揭秘>. 黄轶 ...

  5. Vue源码学习 - 组件化一 createComponent

    Vue源码学习 - 组件化一 createComponent 组件化 createComponent 构造子类构造函数 安装组件钩子函数 实例化 VNode 总结 学习内容和文章内容来自 黄轶老师 黄 ...

  6. Vue源码学习之initInjections和initProvide

    Vue源码学习之initInjections和initProvide 在进行源码阅读之前先让我们了解一个概念:provide/inject,这个是Vue在2.2.0版本新增的一个属性,按照Vue官网的 ...

  7. VUE源码学习第一篇--前言

    一.目的 前端技术的发展,现在以vue,react,angular为代表的MVVM模式以成为主流,这三个框架大有三分天下之势.react和angular有facebook与谷歌背书,而vue是以一己之 ...

  8. Object.defineProperty也能监听数组变化?

    本文简介 点赞 + 关注 + 收藏 = 学会了 首先,解答一下标题:Object.defineProperty 不能监听原生数组的变化.如需监听数组,要将数组转成对象. 在 Vue2 时是使用了 Ob ...

  9. Vue源码学习: 关于对Array的数据侦听

    摘要 我们都知道Vue的响应式是通过Object.defineProperty来进行数据劫持.但是那是针对Object类型可以实现, 如果是数组呢? 通过set/get方式是不行的. 但是Vue作者使 ...

  10. Vue源码学习(三)——数据双向绑定

    在Vue中我们经常修改数据,然后视图就直接修改了,那么这些究竟是怎么实现的呢? 其实Vue使用了E5的语法Object.defineProperty来实现的数据驱动. 那么Object.defineP ...

最新文章

  1. 分布式系统与消息的投递
  2. HTML5本地存储不完全指南
  3. 华为将正式发布鸿蒙手机操作系统;清华成立量子信息班;美团:外卖是微利业务,直接降低抽成无法持续|极客头条...
  4. iOS进阶--提高XCode编译速度、Xcode卡顿解决方案
  5. 2013年c语言课后作业答案,大一C语言课后习题答案.doc
  6. Kali linux 2016.2(Rolling)中的Exploits模块详解
  7. kindle android 音标 乱码,在乐视2手机上解决kindle阅读器即译时音标显示正常的问题...
  8. 如何建设研发部门体系,理清责任和关系
  9. 全球及中国酒店保险箱行业发展现状及需求前景预测报告*2022-2027
  10. Wsus更新失败错误代码:0x80244022故障排除
  11. cx_Freez打包Python批处理
  12. 【安全资讯】incaseformat蠕虫病毒大爆发!20s删除用户文件
  13. fastqc检验时不能执行java_解压fastqc软件包后,运行fastqc报错:没有这个命令?...
  14. 嵌入式系统 操作系统 uC/OS uClinux
  15. linux 技能包【2】:什么是Mbps、Kbps、bps、kb、mb及其换算和区别?
  16. 奇异值分解SVD数学原理及代码(Python)
  17. 皮卡堂什么维修服务器,皮卡堂充值帮助指南
  18. 计算机是如何解读高级语言的
  19. 网络的专家—防火墙!!转自 百度
  20. 模型流程梳理和总结v0

热门文章

  1. 赋能生态 变现为王——云市场生态变现之道
  2. java编解码技术,netty nio
  3. 从零开始学习OpenCL开发(二)一个最简单的示例与简单性能分析
  4. 正则表达式相关:C# RichTextBox显示html文本内容
  5. installshield中用release wizard打包.net framework 1.1中文版
  6. Linux内存映射实现框架
  7. thinkphp 助手函数url不生成https_关于ThinkPHP的一些渗透方式
  8. python封装函数入门_python基础之封装
  9. 深入解读Linux进程调度系列(6)——抢占与非抢占
  10. 注册(三)之设置Contact地址的过期参数