Vue.js观察数组变化主要经过如下7个方法(push、pop、shift、unshift、splice、sort、reverse)数组
怎么实现?浏览器
经过对data数据中数组的这7个方法进行从新包装(注意只是data数据中的数组)缓存
为何不直接对Array.prototype的原型方法进行从新包装?app
由于不该该过多地去影响全局工具
代码实现测试
1 const patchArray = (function () { 2 const methodsToPatch = [ 3 'push', 4 'pop', 5 'shift', 6 'unshift', 7 'splice', 8 'reverse', 9 'sort' 10 ]; 11 12 //设置对象属性的工具方法 13 function def (obj, key, val) { 14 Object.defineProperty(obj, key, { 15 value: val, 16 enumerable: true, 17 writable: true, 18 configurable: true 19 }); 20 } 21 22 const arrayProto = Array.prototype, //缓存Array的原型 23 arrayMethods = Object.create(arrayProto); //继承Array的原型 24 25 methodsToPatch.forEach(function (method, index) { 26 def(arrayMethods, method, function (...args) { 27 //首先调用Array原型的方法 28 const res = arrayProto[method].apply(this, args); 29 //data中每一个数组都有一个__ob__的私有属性指向建立的Observer实例(有兴趣看看源码中的observe方法,这里不详述) 30 const ob = this.__ob__; 31 32 let inserted = null; 33 34 //记录插入的值 35 switch(method) { 36 case 'push': 37 case 'unshift': 38 inserted = args; 39 break; 40 case 'splice': 41 inserted = args.slice(2); 42 break; 43 } 44 45 if (inserted) { 46 //若是是调用了push、unshift、splice,则尝试对新插入的值进行响应式绑定,由于插入的值有多是对象(Object)或者数组(Array) 47 ob && ob.observeArray(inserted); 48 } 49 50 console.log('数组发生改变了'); 51 52 //向全部依赖发送通知,告诉它们数组的值发生变化了 53 ob && ob.dep.notify(); 54 return res; 55 }); 56 }); 57 58 return function (target) { 59 //看看浏览器支不支持__proto__这个属性,经过改变__proto__的值,能够设置对象的原型 60 if ('__proto__' in {}) { 61 //将数组的原型指向arrayMethods,这样当数组调用上述的7个方法时,实际上是调用arrayMethods中的方法而不是调用Array.prototype中的方法 62 target.__proto__ = arrayMethods; 63 } else { 64 //若是浏览器不支持__proto__,则设置数组对应的属性,这样当数组调用上述的7个方法时,实际上是调用数组对应属性指向的方法 65 for (let i = 0, l = methodsToPatch.length; i < l; i++) { 66 let key = methodsToPatch[i]; 67 def(target, key, arrayMethods[key]); 68 } 69 } 70 } 71 })(); 72 73 //测试 74 let arr = [1, 2, 3]; 75 patchArray(arr); 76 arr.push(4);