Vue.js里Array的变化侦测

906 阅读2分钟
可能很多人不太理解为什么Array的侦测方式和Object的不同,下面我们举例来说明一下:
01 this.list.push(1);

在上面的代码中,我们使用push方法向list中新增了数字1. Object的侦测方式是通过getter/setter实现的,但上面的这个例子使用了push方法来改变数组,并不会触发getter.setter。 其实Array的变化侦测是通过拦截器来追踪的。 拦截器其实就是一个和Array.prototype一样的Object,里面包含的属性一模一样,只不过这个Object中某些可以改变数组自身的内容的方法使我们处理过的。 经过整理,我们发现Array原型中可以改变自身内容的方法有7个,分别是:push、pop、shift、unshift、spice、sort、和reverse。

var arrayProto = Array.prototype;
var arrayMethods = Object.create(arrayProto);

var methodsToPatch = [
    'push',
    'pop',
    'shift',
    'unshift',
    'splice',
    'sort',
    'reverse'
];

/**
 * Intercept mutating methods and emit events
 */
methodsToPatch.forEach(function (method) {
    // cache original method
    var original = arrayProto[method];
    def(arrayMethods, method, function mutator () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        var result = original.apply(this, args);
        var ob = this.__ob__;
        var inserted;
        switch (method) {
            case 'push':
            case 'unshift':
                inserted = args;
                break
            case 'splice':
                inserted = args.slice(2);
                break
        }
        if (inserted) { ob.observeArray(inserted); }
        // notify change
        ob.dep.notify();
        return result
    });
});

在上面的代码中,我们创建了变量arrayMethods,它继承自Array.prototype,具备了其所有功能。未来,我们要使用arrayMethods去覆盖Array.prototype。
    接下来,在arrayMethods上使用Object.defineProperty方法将那些可以改变数组自身的内容的方法(push、pop、shift、unshift、splice、sort和reverse)进行封装。
    所以,当使用push方法的时候,其实调用的是arrayMethods.push,而arrayMethods.push是函数mutator,也就是说,实际上执行的是mutator函数。
    最后,在mutator中执行original(它是原生Array.prototype上的方法, 例如Array.prototype.push)来做它应该做的事情,比如push的功能。
    因此,我们就可以在mutator函数中做一些其他的事情, 比如说发送变化通知。