只有索引为0,1,2的属性才会触发 getter
。
这里我们可以对比对象来看,arr数组初始值为[1, 2, 3],即只对索引为0,1,2执行了 observe
方法,所以无论后来数组的长度发生怎样的变化,依然只有索引为0,1,2的元素发生变化才会触发,其他的新增索引,就相当于对象中新增的属性,需要再手动 observe
才可以。
4. 数组的 pop 方法
当移除的元素为引用为2的元素时,会触发 getter
。
删除了索引为2的元素后,再去修改或获取它的值时,不会再触发 setter
和 getter
。
这和对象的处理是同样的,数组的索引被删除后,就相当于对象的属性被删除一样,不会再去触发 observe
。
到这里,我们可以简单的总结一下结论。
Object.defineProperty
在数组中的表现和在对象中的表现是一致的,数组的索引就可以看做是对象中的 key
。
getter
和 setter
方法
通过 push
或 unshift
会增加索引,对于新增加的属性,需要再手动初始化才能被 observe
。
通过 pop
或 shift
删除元素,会删除并更新索引,也会触发 setter
和 getter
方法。
所以, Object.defineProperty
是有监控数组下标变化的能力的,只是vue2.x放弃了这个特性。
二、vue对数组的observe做了哪些处理?
vue的 Observer
类定义在 core/observer/index.js
中。
可以看到,vue的 Observer
对数组做了单独的处理。
hasProto
是判断数组的实例是否有 __proto__
属性,如果有 __proto__
属性就会执行 protoAugment
方法,将 arrayMethods
重写到原型上。 hasProto
定义如下。
arrayMethods
是对数组的方法进行重写,定义在 core/observer/array.js
中, 下面是这部分源码的分析。
/* * not type checking this file because flow doesn't play well with * dynamically accessing methods on Array prototype */ import { def } from '../util/index' // 复制数组构造函数的原型,Array.prototype也是一个数组。 const arrayProto = Array.prototype // 创建对象,对象的__proto__指向arrayProto,所以arrayMethods的__proto__包含数组的所有方法。 export const arrayMethods = Object.create(arrayProto) // 下面的数组是要进行重写的方法 const methodsToPatch = [ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse' ] /** * Intercept mutating methods and emit events */ // 遍历methodsToPatch数组,对其中的方法进行重写 methodsToPatch.forEach(function (method) { // cache original method const original = arrayProto[method] // def方法定义在lang.js文件中,是通过object.defineProperty对属性进行重新定义。 // 即在arrayMethods中找到我们要重写的方法,对其进行重新定义 def(arrayMethods, method, function mutator (...args) { const result = original.apply(this, args) const ob = this.__ob__ let inserted switch (method) { // 上面已经分析过,对于push,unshift会新增索引,所以需要手动observe case 'push': case 'unshift': inserted = args break // splice方法,如果传入了第三个参数,也会有新增索引,所以也需要手动observe case 'splice': inserted = args.slice(2) break } // push,unshift,splice三个方法触发后,在这里手动observe,其他方法的变更会在当前的索引上进行更新,所以不需要再执行ob.observeArray if (inserted) ob.observeArray(inserted) // notify change ob.dep.notify() return result }) })