vue利用defineProperty实现了监听器的功能。但当使用push、pop等方法来修改数组对象时,不会触发setter方法,即这时监听不到数组的变化。
vue对push、pop、shift、unshift、splice、sort、reverse这几个原型方法进行了改写。
改写真实数组的__proto__,如
arr.__proto__=arrayMethods;
让arrayMethods的__proto__指向Array.prototype
const arrayProto = Array.prototype;
export const arrayMethods = Object.create(arrayProto);
定义arrayMethods上的一些方法
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
methodsToPatch.forEach(function (method) {
const original = arrayProto[method]
def(arrayMethods, method, function mutator (...args) { //在arrayMethods上定义push等方法
const result = original.apply(this, args) //构造函数继承数组原型上的这些方法
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
//在原来的push等基础上增加以下功能
//监听变化,通知变化
if (inserted) ob.observeArray(inserted)
ob.dep.notify()
return result
})
})
这时,一个数组对象的结构如下图。
当我们访问push方法时,访问到的是改写后的方法,会给新插入的元素添加监听器,并通知变化。当访问map等方法时,访问的是arr.proto.proto.map(即arrayMethods.proto.map),也就是原型上的map方法。