根據(jù)上一節(jié),我們知道了使用defineProperty來(lái)監(jiān)聽(tīng)數(shù)據(jù)的變化。大概知道響應(yīng)式的基本原理。這一節(jié)我們可以通過(guò)另外一種方式來(lái)做,既使用ES Next的新特性-----Proxy做數(shù)據(jù)代理。去實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽(tīng)。
Proxy
Proxy 對(duì)象用于創(chuàng)建一個(gè)對(duì)象的代理,從而實(shí)現(xiàn)基本操作的攔截和自定義(如屬性查找、賦值、枚舉、函數(shù)調(diào)用等)。
語(yǔ)法:
const p = new Proxy(target, handler)
參數(shù):
target ( 要使用 Proxy 包裝的目標(biāo)對(duì)象(可以是任何類型的對(duì)象,包括原生數(shù)組,函數(shù),甚至另一個(gè)代理)。)
handler ( 一個(gè)通常以函數(shù)作為屬性的對(duì)象,各屬性中的函數(shù)分別定義了在執(zhí)行各種操作時(shí)代理 p 的行為。)
handler
handler 對(duì)象是一個(gè)容納一批特定屬性的占位符對(duì)象。它包含有 Proxy 的各個(gè)捕獲器(trap)。
所有的捕捉器是可選的。如果沒(méi)有定義某個(gè)捕捉器,那么就會(huì)保留源對(duì)象的默認(rèn)行為。
捕捉器等更多詳細(xì) 請(qǐng)查看鏈接 Proxy詳細(xì)說(shuō)明
首先我們先嘗試使用Proxy來(lái)完成上一節(jié)代碼的重構(gòu)。
let data = {
name: 'fzs',
info: {
age: 18,
driving: 2,
familyTies: {
maritalStatus: '已婚'
},
hobby: ['乒乓球', '游泳']
},
}
const observe = data => {
if (!data || Object.prototype.toString.call(data) !== '[object Object]') {
return
}
Object.keys(data).forEach(key => {
let currentValue = data[key]
// 事實(shí)上, Proxy也可以對(duì)函數(shù)類型進(jìn)行代理。這里只對(duì)承載數(shù)據(jù)類型的Object進(jìn)行處理。大家了解即可
if (typeof currentValue === 'object') {
// 遞歸處理對(duì)象類型,通過(guò)proxy代理
observe(currentValue)
data[key] = new Proxy(currentValue, {
set: (target, property, value, reciver) => {
// setter中執(zhí)行各種操作,存儲(chǔ)等
console.log('這是賦值操作')
if (value === currentValue) {
console.log('不需要重新賦值處理')
return true
}
if (property !== 'length') {
console.log('數(shù)組只需要執(zhí)行一次賦值操作,因?yàn)閜ush方法會(huì)引起length的變化,觸發(fā)兩次set操作,我們只需要保留一次即可')
// 這里進(jìn)行我們數(shù)組的賦值攔截的其他操作
}
return Reflect.set(target, property, value) //todo 這是賦值操作
},
})
} else {
Object.defineProperty(data, key, {
enumerable: false,
configurable: false,
get() {
console.log(`getting ${key} value now, getting value is:`, currentValue)
return currentValue
},
set(newValue) {
currentValue = newValue
console.log(`setting ${key} value now, setting value is`, currentValue)
}
})
}
})
}
observe(data)
// 對(duì)數(shù)組進(jìn)行如下操作
if (data.info.hobby) {
data.info.hobby.push('打籃球')
}
觀察輸出我們發(fā)現(xiàn)程序已經(jīng)監(jiān)聽(tīng)到了深層數(shù)據(jù)的變動(dòng)。
簡(jiǎn)單總結(jié)一下:
1.對(duì)于數(shù)據(jù)鍵值類型為基本類型的情況,我們可以繼續(xù)使用
Object.defineProperty
2.對(duì)于鍵值為對(duì)象類型的情況,我們可以繼續(xù)遞歸調(diào)用observe方法,并通過(guò)Proxy返回的新對(duì)象對(duì)data[key]重新賦值,這個(gè)新值的getter和setter已經(jīng)被添加了代理。
了解了Proxy實(shí)現(xiàn)之后,我們對(duì)使用Proxy實(shí)現(xiàn)數(shù)據(jù)代理和使用Object.defineProperty實(shí)現(xiàn)數(shù)據(jù)攔截進(jìn)行對(duì)比,可以得出一下 結(jié)論。
-
Object.defineProperty不能監(jiān)聽(tīng)數(shù)組的變化,需要對(duì)數(shù)組方法進(jìn)行重寫。 -
Object.defineProperty必須遍歷對(duì)象的每個(gè)屬性,且需要對(duì)嵌套結(jié)構(gòu)進(jìn)行深層遍歷。 -
Proxy的代理是針對(duì)整個(gè)對(duì)象的,而不是針對(duì) 對(duì)象 的某個(gè)屬性。因此不像Object.defineProperty必須遍歷對(duì)象的每個(gè)屬性,Proxy只需要做一層代理就可以監(jiān)聽(tīng)同級(jí)結(jié)構(gòu)下的所有屬性變化。當(dāng)然對(duì)于深層結(jié)構(gòu),遞歸還是需要進(jìn)行的。 -
Proxy支持代理數(shù)組的變化。 -
Proxy的第二個(gè)參數(shù)除了可以使用 set 和 get, 還可以使用13種攔截方法,比Object.defineProperty更強(qiáng)大。 Proxy詳細(xì)說(shuō)明 - 使用
Proxy時(shí),性能將會(huì)被底層持續(xù)優(yōu)化;而使用Object.defineProperty時(shí),性能已經(jīng)不再是優(yōu)化重點(diǎn)。