先看下拦截数组变异处理的源码:
const arrayProto = Array.prototype //缓存 Array.prototype
// 实现 arrayMethods.__proto__ === Array.prototype //(拷贝了数组原型下的方法)
export const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
'push', //定义了数组所有的变异方法
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
/** * Intercept mutating methods and emit events */
methodsToPatch.forEach(function (method) { //遍历数组
// 缓存了数组原本的变异方法
const original = arrayProto[method]
//使用 def 函数在 arrayMethods 对象上定义与数组变异方法同名的函数,从而做到拦截的目的
def(arrayMethods, method, function mutator (...args) {
//上定义与数组变异方法同名的函数,在函数体内优先调用了缓存下来的数组变异方法
const result = original.apply(this, args)
//定义了 ob 常量,它是 this.__ob__ 的引用
const ob = this.__ob__
let 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对象
})
})复制代码
这段源码主要是对数组变异的拦截处理,定义了一个数组储存了数组变异的方法,遍历得到每一个变异方法。添加到数组原型下面 主要是为了防止在某些浏览器没有__proto__的情况下兼容。
可以看下数组变异拦截方法(
Observer类的
constructor
函数
)
const arrayKeys = Object.getOwnPropertyNames(arrayMethods)//获取属性名称(要拦截的数组变异方法名字)复制代码
constructor (value: any) {
this.value = value
this.dep = new Dep()
this.vmCount = 0
def(value, '__ob__', this) //为其定义一个__ob__
if (Array.isArray(value)) {//判断是否为数组
if (hasProto) {//检测是否可用__proto__属性 protoAugment(value, arrayMethods) } else { copyAugment(value, arrayMethods, arrayKeys) }
//触发依赖(观察者)观测 this.observeArray(value)
} else {
this.walk(value)
}
}复制代码
protoAugment方法(__proto__)可用情况下复制代码
function protoAugment (target, src: Object) { /* eslint-disable no-proto */ target.__proto__ = src /* eslint-enable no-proto */}
//设置数组实例的 __proto__ 属性,让其指向一个代理原型,从而做到拦截复制代码
copyAugment方法(__proto__)不可用情况下
复制代码
function copyAugment (target: Object, src: Object, keys: Array<string>) { for (let i = 0, l = keys.length; i < l; i++) { const key = keys[i] def(target, key, src[key]) }}
//copyAugment 函数的第三个参数 keys 就是定义在 arrayMethods 对象上的所有函数的键,即所有要拦截的数组变异方法的名称。这样通过 for 循环对其进行遍历,并使用 def 函数在数组实例上定义与数组变异方法同名的且不可枚举的函数,这样就实现了拦截操作。(主要为不支持__proto__)做兼容复制代码