新聞中心
1.寫(xiě)在前面
2.Proxy代理
Proxy代理就是通過(guò)Proxy對(duì)一個(gè)原始對(duì)象進(jìn)行基本操作的攔截和自定義(如屬性查找、賦值、枚舉、函數(shù)調(diào)用等)。

創(chuàng)新互聯(lián)建站是一家專(zhuān)注于網(wǎng)站設(shè)計(jì)、做網(wǎng)站與策劃設(shè)計(jì),武城網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)建站做網(wǎng)站,專(zhuān)注于網(wǎng)站建設(shè)10余年,網(wǎng)設(shè)計(jì)領(lǐng)域的專(zhuān)業(yè)建站公司;建站業(yè)務(wù)涵蓋:武城等地區(qū)。武城做網(wǎng)站價(jià)格咨詢(xún):13518219792
const p = new Proxy(target, handler);
在上面代碼片段中,Proxy可以接收兩個(gè)參數(shù)target、handler:
- target:表示要進(jìn)行代理的原始對(duì)象(可以是任意類(lèi)型對(duì)象,函數(shù)、數(shù)組等)。
- handler:通常以函數(shù)作為屬性的對(duì)象,該對(duì)象是一組夾子(trap),各屬性中的函數(shù)分別定義了在執(zhí)行各種操作時(shí)代理 p 的行為。
那么接下來(lái),我們就來(lái)使用下吧。
const data = {
name:"pingping",
age:18
}
const state = new Proxy(data, {
//攔截屬性取值操作
get(target, key){
//在這里攔截打印數(shù)據(jù)
console.log(`我的${key}是:${target[key]}`);
return target[key];
},
//攔截屬性設(shè)置操作
set(target, key, value){
//在這里攔截設(shè)置數(shù)據(jù)
target[key] = value;
console.log(`我的${key}數(shù)據(jù)更改為${target[key]}`);
return value
}
});
state.name;
state.name = "onechuan"打印數(shù)據(jù)為:
控制臺(tái)打印數(shù)據(jù)
在上面代碼中,我們只用到了get的前兩個(gè)參數(shù)target和target,分別表示代理的原始對(duì)象、被獲取的屬性名。其實(shí)Proxy的get方法中還可以傳入第三個(gè)參數(shù)receiver,表示Proxy代理之后的對(duì)象或繼承Proxy的對(duì)象。
- target:被代理的原始對(duì)象。
- property:被獲取的屬性名。
- receiver:Proxy代理后的對(duì)象或者繼承Proxy的對(duì)象。
const p = new Proxy(target, {
get: function(target, property, receiver) {
}
});我們進(jìn)行個(gè)簡(jiǎn)單的實(shí)踐:
const data = {
name:"pingping",
age:18
}
const state = new Proxy(data, {
get: function(target, property, receiver) {
console.log(state === receiver);
return target[property];
}
});
state.name;此時(shí),看到控制臺(tái)打印的結(jié)果是:
控制臺(tái)打印結(jié)果
在上面舉的例子中,的確receiver指的是Proxy代理之后的對(duì)象state,當(dāng)然receiver也可以指向的是繼承Proxy的對(duì)象。
const data = {
name:"pingping",
age:18
}
const state = new Proxy(data, {
get: function(target, property, receiver) {
console.log(state === receiver);
return target[property];
}
});
const obj = {
name:"onechuan"
}
//將obj對(duì)象原型設(shè)置為state對(duì)象,即obj繼承state
Object.setPrototypeOf(obj, state);
obj.name;控制臺(tái)打印結(jié)果:
設(shè)置繼承的打印結(jié)果
在上述代碼和打印結(jié)果中,我們可以看到打印的receiver并不等于state對(duì)象。這是因?yàn)間et方法的第三個(gè)參數(shù)receiver,可以傳遞對(duì)象get調(diào)用者指向,即可以正確傳遞上下文。
receiver不僅表示Proxy代理后的對(duì)象state本身,也會(huì)表示繼承Proxy的對(duì)象。
const data = {
name:"pingping",
age:18,
get value(){
return this.name;
}
}
const state = new Proxy(data, {
get: function(target, property, receiver) {
console.log(this === receiver);//false
console.log(state === receiver);//false
console.log(obj === receiver);//true
return target[property];
}
});
const obj = {
name:"onechuan"
}
//將obj對(duì)象原型設(shè)置為state對(duì)象,即obj繼承state
Object.setPrototypeOf(obj, state);
obj.value;我們看到控制臺(tái)的打印結(jié)果如下:
控制臺(tái)打印結(jié)果
我們分析下上面代碼,在訪問(wèn)obj.value時(shí),在obj對(duì)象上本身是沒(méi)有value屬性的,它會(huì)通過(guò)原型去查找proxy對(duì)象state上的value屬性取值器,會(huì)去觸發(fā)state對(duì)象上的get value()操作符。此時(shí),會(huì)觸發(fā)state對(duì)象的取值攔截器,返回target[property]的值,這樣使用obj.value取值就變成了data.value,最終返回的結(jié)果就是pingping。
具體的Proxy的一些API見(jiàn)MDN文檔吧。
3.Reflect反射
在了解了Proxy后,我們?cè)賮?lái)討論下Proxy的好兄弟Reflect,它們是如何配合工作的。
Reflect是一個(gè)全局內(nèi)置的對(duì)象,它提供攔截JavaScript操作的方法。但是,Reflect本身不是個(gè)函數(shù)對(duì)象,因此其不是一個(gè)構(gòu)造函數(shù),不能使用new進(jìn)行調(diào)用。Reflect的所有屬性和方法都是靜態(tài)的。
Reflect.get(target, propertyKey[, receiver])
- target:需要取值的目標(biāo)對(duì)象
- propertyKey:需要獲取的值的鍵值
- receiver:如果target對(duì)象中指定了getter,receiver則為getter調(diào)用時(shí)的this值
那么,我們就使用Reflect來(lái)獲取對(duì)象的屬性吧:
const state = new Proxy(data, {
get: function(target, property, receiver) {
console.log(this === receiver);//false
console.log(state === receiver);//false
console.log(obj === receiver);//true
return Reflect.get(target, property);
// 等價(jià)于return target[property];
}
});在控制臺(tái)可以看到,打印結(jié)果是一樣的:
控制臺(tái)打印結(jié)果
在這里,使用Reflect.get(target, property)是等價(jià)于return target[property]的,this在Proxy.get攔截器中將this的指向了原始數(shù)據(jù)data對(duì)象,這樣obj.value打印結(jié)果自然也是pingping。
如果我們要獲取obj對(duì)象自身的name屬性,應(yīng)該怎么辦?
onst state = new Proxy(data, {
get: function(target, property, receiver) {
console.log(this === receiver);//false
console.log(state === receiver);//false
console.log(obj === receiver);//true
return Reflect.get(target, property, receiver);
// 等價(jià)于return target[property];
}
});打印結(jié)果:
打印結(jié)果
我們將Proxy.get的第三個(gè)參數(shù)receiver傳入Reflect.get中,此時(shí)我們發(fā)現(xiàn)打印結(jié)果就變成了onechuan。這是因?yàn)镻roxy.get的第三個(gè)參數(shù)receiver,可以表示代理對(duì)象state還可以表示繼承代理對(duì)象state的對(duì)象obj。而在Reflect.get中傳入了Proxy.get的第三個(gè)參數(shù)receiver,即obj對(duì)象作為參數(shù),此時(shí)Reflect.get會(huì)把this的指向改為obj。
Reflect.get(target, key, receiver)其實(shí)可以理解為target[key].call(receiver),而Reflect.get的參數(shù)receiver作用:修改屬性訪問(wèn)時(shí)this的指向receiver。
4.參考文章
《為什么Proxy一定要配合Reflect使用?》
《MDN文檔關(guān)于Proxy的描述》
《MDN文檔關(guān)于Reflect的描述》
《了解學(xué)習(xí) Proxy 的好朋友 - Reflect,為什么需要 Reflect》
5.寫(xiě)在最后
在Vue.js3中使用Proxy來(lái)實(shí)現(xiàn)響應(yīng)式數(shù)據(jù),具體就是通過(guò)Proxy代理原始對(duì)象,通過(guò)攔截和修改對(duì)象的基本操作。在代理過(guò)程中,會(huì)出現(xiàn)取值器的this指向問(wèn)題,此時(shí)需要使用Reflect的方法第三個(gè)參數(shù)receiver來(lái)解決。
名稱(chēng)欄目:Vue.js設(shè)計(jì)與實(shí)現(xiàn)-Proxy和Reflect
網(wǎng)站URL:http://fisionsoft.com.cn/article/dpedpsj.html


咨詢(xún)
建站咨詢(xún)
