先看下拦截数组变异处理的源码:
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) { 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__)做兼容复制代码