JS資料劫持 之 Proxy設定代理
阿新 • • 發佈:2022-11-29
1、簡介
資料劫持,指的是在訪問或者修改物件的某個屬性時,通過一段程式碼攔截這個行為,進行額外的操作或者修改返回結果。
經典的應用是Vue雙向資料繫結。
常見的資料劫持實現方法有兩種:
- Object.defineProperty
- Proxy設定代理(ES6新增)
本篇文章介紹Proxy的使用。
2、Proxy
2.1、介紹
new Proxy(target, handler)
-
引數:
- target:要使用 Proxy 包裝的目標物件(可以是任何型別的物件,包括原生陣列,函式,甚至另一個代理)。
- handler:一個通常以函式作為屬性的物件,各屬性中的函式分別定義了在執行各種操作時代理的行為。
-
handler中常⽤的物件⽅法:
- get(target, propKey, receiver):攔截物件的讀取屬性操作。
- set(target, propKey, value, receiver):設定屬性值操作的捕獲器。
- has(target, propKey):對in操作符的代理方法。
- construct(target, args):攔截new操作符,返回的是一個物件。
- apply(target, object, args):攔截函式的呼叫。
2.2、程式碼示例
2.2.1、get(target, propKey, receiver)
攔截物件的讀取屬性操作。
- target:目標物件。
- propKey:要獲取的key值。
- receiver:Proxy代理自身。
const target = { name: 'cxk' }; const handler = { get(target, propKey, receiver) { console.log('---呼叫了get方法---'); console.log(target, propKey, receiver); return target[propKey]; } }; const proxy = new Proxy(target, handler); console.log(target.name); // cxk console.log(proxy.name); // ---呼叫了get方法--- // {name: 'cxk'} 'name' Proxy {name: 'cxk'} // cxk
2.2.2、set(target, propKey, value, receiver)
設定屬性值操作的捕獲器。
- target:目標物件。
- propKey:要新增或修改的key值。
- value:要把key設定成什麼值。
- receiver:Proxy代理自身。
const target = {
name: 'cxk'
};
const handler = {
set(target, propKey, value, receiver) {
console.log('---呼叫了set方法---');
console.log(target, propKey, value, receiver);
target[propKey] = value;
}
};
const proxy = new Proxy(target, handler);
// 通過proxy新增或修改屬性,觸發set方法,並修改成功
proxy.age = 18;
console.log(proxy); // Proxy {name: 'cxk', age: 18}
console.log(target); // {name: 'cxk', age: 18}
// 通過target新增或修改屬性,不觸發set方法,也修改成功
target.age = 20;
console.log(proxy); // Proxy {name: 'cxk', age: 20}
console.log(target); // {name: 'cxk', age: 20}
2.2.3、has(target, propKey)
對in操作符的代理方法。
- target:目標物件。
- propKey:in操作符左邊的引數。
const target = {
name: 'cxk',
_age: 18
};
const handler = {
has(target, propKey) {
console.log('---觸發了has方法---');
console.log(target, propKey);
// 物件內部的私有屬性以_開頭,外部不可以訪問。
if (propKey.includes('_')) {
return false;
}
return propKey in target;
}
};
const proxy = new Proxy(target, handler);
console.log('_age' in target); // true
console.log('_age' in proxy); // false
// ---觸發了has方法---
// {name: 'cxk', _age: 18} '_age'
// false
2.2.4、construct(target, args)
攔截new操作符,返回的是一個物件。
- target:目標物件,也就是建構函式。
- args:被呼叫時的引數,是一個類陣列。
function target(name) {
this.name = name;
}
const handler = {
construct(target, args) {
console.log('---觸發了construct方法---');
console.log(target, args);
return new target(...args);
}
};
const proxy = new Proxy(target, handler);
new proxy('cxk', 18);
// ---觸發了construct方法---
// ƒ target(name) {
// this.name = name;
// } (2) ['cxk', 18]
2.2.5、apply(target, object, args)
攔截函式的呼叫。
- target:目標物件,也就是函式。
- object:目標物件的上下文物件(this)。
- args:被呼叫時的引數,是一個類陣列。
function sum(a, b) {
return a + b;
}
const handler = {
apply(target, object, args) {
console.log('---觸發了apply方法---');
console.log(target, object, args);
return target(...args);
}
};
const proxy = new Proxy(sum, handler);
console.log(proxy(1, 2));
// ---觸發了apply方法---
// ƒ sum(a, b) {
// return a + b;
// }
// undefined
// [1, 2]
// 3
let obj = {
a: 1,
b: 2
}
console.log(proxy.call(obj, 3, 3));
// ---觸發了apply方法---
// ƒ sum(a, b) {
// return a + b;
// }
// {a: 1, b: 2}
// [3, 3]
// 6
- Proxy代理的handler物件方法很多,上述只列舉了最常用的幾個,其他不怎麼常用的物件方法的需要讀者自行搜尋學習。