Array的变化侦测

2019-07-27  本文已影响0人  李友胜

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)
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读