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);