可能很多人不太理解为什么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函数中做一些其他的事情, 比如说发送变化通知。