上一篇的末尾,笔者简单介绍了Object.defineProperty
在数组监控方面的不足以及其替代品Proxy
。可是对于前者总感受还少点什么,emmmmm...好像是demo,因而笔者精心准备了一下。因此本篇会主要会分红两大块:一是讲述如何弥补Object.defineProperty
先天不足的状况下实现对数组的精准监控,二是着重用Proxy
实现双向数据绑定。node
首先呢,对于Object.defineProperty
在数组监控方面的不足,咱们不只要知其然,更要知其因此然。所以先用栗子来证明下这个不足
。上代码:
let data = { list: [] } Object.keys(data).forEach(function (key) { let value = data[key]; Object.defineProperty(data, key, { enumerable: true, configurable: true, get() { return value; }, set(newValue) { console.log(`Setting`); value = newValue; return true; } }) }) data.list.push(1); //---> A // data.list = [1, 2, 3]; //---> B console.log(data.list);
事先咱们须要明确下:因此对数组的监控,就是能监测到数组元素的 增长或者删除。所以按照‘理论’,上述代码若是向list
中增长元素的时候,理应会有打印Setting
(笔者已经标记了最后的A
行 和B
行)
在只注释 B
行状况下看运行结果:git
/usr/local/bin/node --inspect-brk=17809 demo.js Debugger listening on ws://127.0.0.1:17809/48911764-8533-4c95-a501-20384c924f6a Debugger attached. Array(1) [1]
能够看出,虽然咱们成功得向数组里增长了一个元素,可是并无打印出 Setting
,所以就是说并无监测到数组的变化.es6
在只注释 A
行状况下看运行结果:segmentfault
/usr/local/bin/node --inspect-brk=27303 demo.js Debugger listening on ws://127.0.0.1:27303/2fac4f06-e775-4485-b70b-b2660a98c2b8 Debugger attached. Setting Array(3) [1, 2, 3]
咱们成功得向数组里增长了一个元素也没有打印出 Setting
,说明数组的变化被监测到了.源码数组
因此咱们能够大胆得猜想:当监控 数组 数据对象的时候,实质上就是监控数组的 地址,地址不变也就不会被监测到,因此咱们向list
里 push 元素的时候并无触发打印;当咱们直接替换list
对象的时候就触发了打印。因此这就是Object.defineProperty
在数组监控方面的不足。
下面就用例子介绍下如何弥补这方面的不足(固然,也是Vue的处理方式)app
其核心思想就是
覆写
数组对象中的方法,在调用数组方法的同时能触发回调。下面是核心代码:
let arrayMethod = Object.create(Array.prototype); ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(function (method) { Object.defineProperty(arrayMethod, method, { enumerable: true, configurable: true, value: function () { let args = [...arguments] Array.prototype[method].apply(this, args); console.log(`operation: ${method}`) dep.notify(); } }) }); [Array Object].__proto__ = arrayMethod;
同时这样作的好处是,仅仅是数据中的数组对象的原型被修改掉了,并不会影响到全局的数组对象。
而后笔者也作了一个例子,以下:框架
经过向 list
中添加随机数或者删除随机数,能同步渲染到页面上,源码在这函数
Proxy
意为代理
,通俗来讲就是在 目标数据对象 外围设置一层 拦截。
举个形象的栗子:有一个仓库
,一开始你们能够任意得存放货物,后来老板请了一个仓库管家
,全部人想要存放货物的必需要通过管家的手。并且老板还给管家制定了一套管理标准
,管家对仓库的管理必须严格按照这套标准。
根据这个栗子,罗列了三个关键词:仓库
,仓库管家
,管理标准
,下面用一段简单的代码来表述下, 若是想全面得学习Proxy, 请参考阮一峰老师的博客:学习
let dataProxy = new Proxy(data, { set() { }, get() { } })
那么代码与栗子的对于关系就出来了:this
仓库 --- data
仓库管家 --- Proxy / dataProxy
管理标准 --- {set(),get()}
并且能够注意到 Proxy 实际上是个构造函数,全部咱们对原数据对象的操做都得经过构造函数new
出来的对象 dataProxy
。就好像全部货物的存取都得经过管家同样
而后接下来笔者用 Proxy 写了一个栗子,用来 重现 上面对数组的监控,核心代码以下:
//数据源 let vm = { list: [1, 2, 3, 4] } let vmProxy = new Proxy(vm.list, { set(target, prop, value) { console.log(`Setting: ${value}`); Reflect.set(target, prop, value); dep.notify(); return true; } })
咱们能够注意到咱们并无直接将 vm
用Proxy进行“包装”,而是将vm.list
进行“包装”。由于笔者在学习的时候发现若是直接用 vm
的话,当咱们向 list
中添加元素的时候并不会被监测到,笔者猜想的缘由和 Object.defineProperty
同样,栗子呈上。若是有知道缘由的小伙伴,请多多指教。
篇幅有限,这个例子的代码直接上连接了,有兴趣的小伙伴能够直接down下来看。
数组说完了,剩下就普通类型的数据就是按照正常套路走就对了。另外,笔者此次吸收上篇博客的教训,将代码写得更详尽,实现方式更贴近框架级别。用Proxy实现数据的双向绑定,栗子的功能和上篇博客是同样的,只是内容更加饱满。该栗子中笔者简单实现了一个简单版的类Vue的框架 View
,包含Html文档的解析、Watcher的构造、数据的绑定等,代码简单不复杂,因此这里就不赘述了,我以为千言万语不敌一段代码,那么相关的代码就此呈上。
感谢小伙伴的捧场!我会坚持写博客,分享本身的见闻和工做中遇到的坑,共同窗习,共同进步