3.1 如何追踪变化
object是通过setter来追踪变化的,只要数据发生变化,就一定会触发setter。
同理,数组中push或pop等等改变数组内容,我们只要能在用户操作数组的时候得到通知就能实现目的。
我们可以通过用一个拦截器覆盖Array.prototype。之后,每当使用Array原型上的方法操作数组,其实执行的都是拦截器中的方法。
3.2 拦截器
拦截器其实就是一个和Array.prototype一样的Object,里面包含的属性一模一样,只不过这个Object中某些可以改变数组吱声的方法是我们处理过的,经过整理,我们发现Array原型中的可以改变数组自身的方法有7个,push、pop、shift、unshift、splice、sort、reverse。
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto)
;['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(function (method) {
// 缓存原始方法
const original = arrayMethods[method]
Object.defineProperty(arrayMethods, method, {
value: function mutator(...args) {
return original.apply(this, args);
},
enumerable: false,
writable: true,
configurable: true
})
})
在上面代码中,我们可以看出arrayMethods,它继承自Array.prototype,具备其所有的功能,未来我们需要使用arrayMethods覆盖Array.prototype。接下来,在arrayMethods上使用Object.defineProperty方法将那些可以改变自身内容的方法进行封装。所以当调用push方法的时候,其实调用的是arrayMethods.push,而arrayMethods.push方法嗲用的是函数mutator。
最后在mutator中执行original(它是原生Array.prototype上的方法,例如Array.prototype.push)来做他应该做的事。所以我们可以在mutator函数中做一些其他的事情,比如发送变化通知。
3.3 使用拦截器覆盖Array原型
有了拦截器以后,就需要使用它覆盖Array.prototype。但是不能直接覆盖,因为这样会污染全局的Array。我们希望之拦截那些被侦测了变化的数据,也就是之拦截那些响应式的数组的原型。
而将一个数据转化成响应式的,需要通过Observer,所以我们只需要在Observer中使用拦截器覆盖那些即将被转化成Array类型数据的原型就好:
class Observer{
constructor(value) {
this.value = value;
if (Array.isArray(value)){
value.__proto__ = arrayMethods // 新增
}else {
this.walk(value)
}
}
}
网友评论