2023-02-02
proxy 對象
前言
現(xiàn)在vue3.0雖然沒有大規(guī)模使用在項目中,但面試的時候問的可一點也不少.特別是vue中雙向數(shù)據(jù)綁定,幾乎必問.但vue2.0和vue3.0的數(shù)據(jù)綁定還是不一樣的....
一、vue2.x的雙向數(shù)據(jù)綁定
· 在Vue2.x中,雙向數(shù)據(jù)綁定是通過Object.definePropert實現(xiàn)的.
· 用get,set方法中通過發(fā)布訂閱者模式來實現(xiàn)的數(shù)據(jù)響應。
· 但它只能監(jiān)聽存在的屬性,對于新增或刪除的便無能無力.
· 同時數(shù)組的改變他也是沒有辦法監(jiān)聽的.
二、proxy的使用
1.proxy是個什么玩意
· ES6中新增的代理反射,可以然讓我們攔截一些操作,且添加其它的能力.
· 簡單說,我們對某個對象設置攔截器后,它的一些操作就能被我們捕獲到,如此我們可以按照需求對默認的行為進行修改或者其它操作.
· 類似于常見的鉤子函數(shù),發(fā)生某個行為時,觸發(fā)某個鉤子函數(shù).
2.基本形式
語法
const proxy = new Proxy(target, handler)
· target: 要監(jiān)聽的對象,可以是一個對象,數(shù)組,函數(shù)等等
· handler: 是對象,里面包含了可以監(jiān)聽target的方法.
· proxy為返回的新對象, 為了能夠觸發(fā)handler里面的函數(shù),必須要使用返回值去進行其他操作,比如修改值.handler里面的方法可以有以下這十三個,每一個都對應的一種或多種針對proxy代理對象的操作行為
支持的寫法
Proxy提供了十三種攔截對象操作的方法,這里只介紹幾個vue3中常用到的,更多的請參考MDN
1. handler.get()當通過proxy去讀取對象里面的屬性的時候,會進入到get鉤子函數(shù)里面.
2. handler.set當通過proxy去為對象設置修改屬性的時候,會進入到set鉤子函數(shù)里面
3. handler.has當使用in判斷屬性是否在proxy代理對象里面時,會觸發(fā)has,比如
const obj = {
name: '小甜甜'
}
console.log('name' in obj)
3. handler.apply當proxy監(jiān)聽的是一個函數(shù)的時候,當調用這個函數(shù)時,會進入apply鉤子函數(shù)
4. handler.construct當使用new操作符的時候,會進入construct這個鉤子函數(shù)
5. handler.defineProperty當使用Object.defineProperty去修改屬性修飾符的時候,會進入這個鉤子函數(shù)
3.常用API
Reflect 對象
const target = {
id:'target'
}
const handler ={
// 捕獲器在處理程序時,以方法名為鍵
get(){
return 'handle override';
}
}
const proxy = new Proxy(target,handler);
console.log(target.id); // target
console.log(proxy.id); // handle override
· 以上方法中,是基于參數(shù),重建操作,可不是所有的捕獲器行為都像get這么簡單
· 我們可以通過Reflect對象上的同名方法,來繼續(xù)當前的默認行為.
const target = {
id:'target'
}
const handler ={
// 捕獲器在處理程序時,以方法名為鍵
get(){
//Reflect上的同名方法 繼續(xù)原來的操作
return Reflect.get(...arguments);
}
}
const proxy = new Proxy(target,handler);
console.log(target.id); // target
console.log(proxy.id); // target
· 操作符的替代
Reflect.get() 替代對象屬性訪問操作符號.
Reflect.set() 替代= 賦值操作符,返回 bool值
Reflect.has() 替代in操作符或者with()
Reflect.deleteProperty() 替代delete操作符
Reflect.constructor() 替代new操作符
代理捕獲器和反射方法
對于代理對象上的任何一種操作,只會有一個捕獲處理器被調用.
1. get() 在獲取屬性值,操作時調用.對應反射API為Reflect.get()
const target = {
id:'target'
}
const handler ={
// 捕獲器在處理程序時,以方法名為鍵
get(){
console.log('get()被調用');
return Reflect.get(...arguments);
}
}
const proxy = new Proxy(target,handler);
proxy.foo; // get()被調用
2. set() 捕獲器會在設置屬性時調用,對應反射API為Reflect.set()
const target = {
id:'target'
}
const handler ={
// 捕獲器在處理程序時,以方法名為鍵
set(target,property,value,receiver){
console.log('set()被調用');
return Reflect.set(...arguments);
}
}
const proxy = new Proxy(target,handler);
proxy.foo = 'bar';
//target 為目標對象
// property 為屬性
// value 為屬性值
// receiver 接收最初賦值對象
· apply() 會在調用函數(shù)時被調用,對應的反射api為Reflect.apply()
const fn = ()=>{
}
const proxy = new Proxy(fn,{
apply(target,thisArg,...argumentsList){
console.log('apply()');
return Reflect.apply(...arguments)
}
})
proxy(); // apply
· construct() 會在new操作符時被調用;
const fn = function(){
}
const proxy = new Proxy(fn,{
// target必須為構造函數(shù)
construct(target,argumentList,newTarget){
console.log('construct');
return Reflect.construct(...arguments)
}
})
new proxy();
4.使用場景
a. 隱藏屬性,
將目標對象上的屬性進行隱藏.
const hiddenProperites = ['foo','bar'];
const tarObject = {
foo:1,
bar:2,
baz:3
};
const proxy = new Proxy(tarObject,{
get(target,property){
if(hiddenProperites.includes(property)){
return undefined;
}else{
return Reflect.get(...arguments)
}
},
has(target,property){
if(hiddenProperites.includes(property)){
return false;
}else{
return Reflect.has(...arguments)
}
}
});
console.log(proxy.foo); // undefined
console.log(proxy.baz); // 3
console.log('foo' in proxy); // false
console.log('baz' in proxy); // true
b 屬性驗證
所有的賦值操作都會觸發(fā)set,我們可以所賦的值決定是允許還是拒絕.
const target = {
num:0
}
const proxy = new Proxy(target,{
set(target,property,value){
// 是number 類型才允許賦值
if(typeof value !== 'number'){
return false;
}else{
return Reflect.set(...arguments)
}
}
});
proxy.num = 2;
console.log(proxy); // Proxy {num: 2}
proxy.num = 'abc';
console.log(proxy); // Proxy {num: 2}
c 構造函數(shù)必須傳參
和保護和驗證對象的屬性相似,我們也可以對構造函數(shù)的參數(shù)進行審查.
// 類必須傳遞參數(shù)
class User{
constructor(id) {
this._id = id;
}
}
const proxy = new Proxy(User,{
construct(target,argumentsList,newTarget){
if(argumentsList[0]===undefined){
throw '必須傳遞參數(shù)'
}else{
return Reflect.construct(...arguments);
}
}
})
new proxy(); // Uncaught 必須傳遞參數(shù)
d 對象的可觀察
向數(shù)組中每插入一個值,emit方法就會收到消息
const userList = [];
// 接收傳遞的值
function emit(newValue){
console.log(newValue);
}
const proxy = new Proxy(userList,{
set(target,property,value,receiver){
const result = Reflect.set(...arguments)
if(result){
// 設置值時,調用emit方法
emit(Reflect.get(target,property,receiver));
}
return result;
}
});
proxy.push('小甜甜');
三、總結
· 代理是由你創(chuàng)建的特殊對象,它封裝另一個普通對象,或者誰擋在普通對象前面.
· 可以在代理對象上注冊特殊的處理函數(shù),代理商執(zhí)行各種操作時就會調用這個程序.
· 這些程序除了把操作轉發(fā)給原始目標/被封對象外,還會執(zhí)行其它額外操作.
開班時間:2021-04-12(深圳)
開班盛況開班時間:2021-05-17(北京)
開班盛況開班時間:2021-03-22(杭州)
開班盛況開班時間:2021-04-26(北京)
開班盛況開班時間:2021-05-10(北京)
開班盛況開班時間:2021-02-22(北京)
開班盛況開班時間:2021-07-12(北京)
預約報名開班時間:2020-09-21(上海)
開班盛況開班時間:2021-07-12(北京)
預約報名開班時間:2019-07-22(北京)
開班盛況Copyright 2011-2023 北京千鋒互聯(lián)科技有限公司 .All Right 京ICP備12003911號-5 京公網(wǎng)安備 11010802035720號