目的: 了解Object.defineProperty如何實現數據劫持
大致原理是這樣的:
1. 先定義一個對象
let obj = { name: 'jw'}2. 定義一個監聽函數
/*** 判斷監聽的是否是對象* 如果是對象,就遍歷,并且對每個屬性進行定義get 和 set*/function observer(obj) { if(typeof obj === 'object') { for (let key in obj) { // defineReactive 方法設置get和set,見第三步 defineReactive(obj, key, obj[key]); } }}3.定義一個函數,處理每個屬性
function defineReactive(obj, key, value) { Object.defineProperty(obj, key, { get() { return value; }, set(val) { console.log('數據更新了') value = val; } })}ok. 到這里初版已經實現了。嘗試一下吧
observer(obj);obj.name = 'haha'
控制臺輸出:
//數據更新了
以上已經實現設置obj的屬性的時候,被監聽到,并且可以去執行一些代碼了。但是,如果對象里面嵌入了對象呢?比如:
let obj = { name: 'jw', age: { old: 60 }}執行以下代碼
observer(obj);obj.age.old = '50'
控制臺輸出: 空
4.對監控的obj進行迭代處理
// 修改defineReactive , 添加一行代碼function defineReactive(obj, key, value) { // 如果對象的屬性也是一個對象。迭代處理 observer(value); Object.defineProperty(obj, key, { //.... })}再執行以下代碼:
observer(obj);obj.age.old = '50'
控制臺輸出:
//數據更新了
可惜的是,如果對象是一個數組,Object.defineProperty就無法起作用了,比如:
obj.skill = [1, 2, 3];obj.age.push(4);
控制臺輸出:
//空
實際上,不止push,包括slice,shift,unshif...都是沒有作用.
5. 重寫數組的方法
let arr = ['push', 'slice', 'shift', 'unshift'];arr.forEach(method=> { let oldPush = Array.prototype[method]; Array.prototype[method] = function(value) { console.log('數據更新了') oldPush.call(this, value) }})再執行以下代碼:
obj.skill = [1, 2, 3];obj.skill.push(4);
控制臺輸出:
//數據更新了
但是,數組的length操作仍然是無效的。這也是為什么vue中只能通過方法去改變數組的原因了。
新聞熱點
疑難解答
圖片精選