目的: 瞭解Object.defineProperty如何實現數據劫持javascript
閱讀時間: 3 分鐘前端
大體原理是這樣的:vue
1. 先定義一個對象java
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. 到這裏第一版已經實現了。嘗試一下吧ui
observer(obj);
obj.name = 'haha'
控制檯輸出:
//數據更新了
複製代碼
以上已經實現設置obj的屬性的時候,被監聽到,而且能夠去執行一些代碼了。可是,若是對象裏面嵌入了對象呢?好比: this
let obj = {
name: 'jw',
age: {
old: 60
}
}
複製代碼
執行如下代碼spa
observer(obj);
obj.age.old = '50'
控制檯輸出: 空
複製代碼
4.對監控的obj進行迭代處理prototype
// 修改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.skill.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中只能經過方法去改變數組的緣由了。
總結: Object.defineProperty只是解決了狀態變動後,如何觸發通知的問題,那要通知誰呢?誰會關心那些屬性發生了變化呢?之後再說。
感謝閱讀!
我是海明月, 前端小學生。
let obj = {
name: 'jw',
age: {
old: '60'
}
}
// vue 數據劫持 Observer.defineProperty
function observer(obj) {
if(typeof obj === 'object') {
for (let key in obj) {
defineReactive(obj, key, obj[key]);
}
}
}
function defineReactive(obj, key, value) {
observer(value);
Object.defineProperty(obj, key, {
get() {
return value;
},
set(val) {
console.log('數據更新了')
value = val;
}
})
}
observer(obj);
// obj.age.old = '50'
// Object.defineProperty 對 數組無效
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);
複製代碼