前言
Proxy對象是ES6(ECMAScript 6)中提供用來操做對象的一個API,其含義官方給出的解釋是:Proxy 對象用於定義基本操做的自定義行爲(如屬性查找,賦值,枚舉,函數調用等)
。
es6
Proxy
本意是代理
,那既然代理,確定要具有代理的倆基本素質,1.幫誰代理,2.乾點什麼。因此, 幫誰代理? 幫目標對象代理
,乾點什麼?架設攔截,對外界的訪問進行過濾和改寫
,串起來講就是:在目標對象以前架設一層「攔截」,外界對該對象的訪問,都必須先經過這層攔截,所以提供了一種機制,能夠對外界的訪問進行過濾和改寫
。這下明白了吧,嘿嘿。let proxy = new Proxy(target, handler);
複製代碼
tgarget
:要代理的目標對象。(能夠是任何類型的對象,包括原生數組,函數,甚至另外一個代理)。
handler
:定義攔截行爲的配置對象(也是一個對象,其內部的屬性均爲執行操做的函數)。數組
含義:攔截對象屬性的讀取
target
:要代理的目標對象。
key
:屬性名。
receiver
:proxy實例(可選參數,通常不用)
bash
const proxy1 = new Proxy({
a: 1
}, {
get(target, key) {
if(Reflect.has(target,key)) {
return Reflect.get(target,key);
}else {
return false;
}
}
})
proxy1.a //1
proxy1.b //false
複製代碼
以上,a屬性由於有定義,因此可獲取到其值爲1,b屬性沒有被定義,輸出爲false
,若是這裏沒有定義get方法,那麼獲取操做就會被轉發到目標對象身上,默認若是沒有此屬性,會返回undefined
,而這裏卻返回false
,正是由於從新定義了get方法,對返回內容進行了修改,正如其含義,對代理對象屬性的讀取進行了攔截。app
含義:攔截對象屬性的設置
target
:要代理的目標對象。
key
:要設置的屬性名。
value
:要設置的屬性值。
receiver
:proxy實例(可選參數,通常不用)
函數
const handler = {
set(target,key,value) {
if(!Reflect.has(target,key)) {
Reflect.set(target,key,value);
}else {
console.log(`${key}屬性已存在`) //a屬性已存在
}
},
get(target,key) {
return Reflect.get(target,key);
}
}
const proxy = new Proxy({},handler);
proxy.a = 1;
console.log(proxy.a);//1
proxy.a = 2;
console.log(proxy.a) //1
複製代碼
以上,設置屬性的時候若是當前屬性已存在,則不能設置成功,所以,咱們能夠利用set方法來攔截設置符合咱們指望的屬性,根據應用場景,自由發揮。學習
var obj = {};
const target = Object.defineProperty(obj, 'a', {
value: 123,
writable: false,
});
const handler = {
set(target,key,value) {
Reflect.set(target,key,value);
},
get(target, key) {
return Reflect.get(target,key);
}
};
const proxy = new Proxy(target, handler);
proxy.a = 456;
console.log( proxy.a) //123
複製代碼
以上,當目標對象的某個屬性爲不可寫狀態,那麼set方法將會失效。ui
不可寫且不可配置
狀態,set方法將會失效,但自測發現其結果與configurable
屬性的狀態並沒有關係,只與writable
屬性有關,有點小疑惑,但願看到的大佬能幫小弟解惑。含義:判斷對象是否具備某個屬性。
target
:要代理的目標對象。
key
:要設置的屬性名。
當判斷一個對象中是否具備某個屬性時,has方法就會生效,典型的操做就是in運算符應用,返回值爲布爾類型this
const handler = {
has(target, key) {
if (key[0] === '_') {
console.log('it is a private property')
return false;
}
return key in target;
}
};
const target = {
_a: 123,
a: 456
};
const proxy = new Proxy(target, handler);
console.log('_a' in proxy) // it is a private property false
console.log('a' in proxy);//true
複製代碼
以上,當對proxy使用in
運算符時,就會自動觸發has方法,若是判斷當前屬性以_開頭的話,就進行攔截,從而不被後面的in
運算符發現,實現隱藏某些特定屬性的目的。spa
不過須要注意,當目標對象是不可擴展或者對象的屬性是不可配置時has方法不能隱藏目標對象的當前屬性,不然攔截會報錯。代理
var obj = {
a: 10
};
Object.preventExtensions(obj); //使obj對象不可擴展,也就是使其不能增長新的屬性
var p = new Proxy(obj, {
has: function (target, key) {
return false;
}
});
'a' in p // Uncaught TypeError: 'has' on proxy: trap returned falsish for property 'a' but the proxy target is not extensible
let obj = {};
Object.defineProperty(obj, "a", {
configurable: false, //當前屬性不可配置
value: 10,
});
var p = new Proxy(obj, {
has: function (target, key) {
return false;
}
});
'a' in p // Uncaught TypeError: 'has' on proxy: trap returned falsish for property 'a' which exists in the proxy target as non-configurable
複製代碼
含義:攔截函數的調用、call和apply操做
target
:目標對象。
thisArgs
:目標對象的上下文對象(this)。
args
:目標對象的參數數組。
const handler = {
apply(target, ctx, args) {
return Reflect.apply(...arguments) * 2;
//或者
return target(...args) * 2
}
};
function sum(...args) {
let num = 0;
args.forEach((item) => {
num += item;
})
return num;
}
var proxy = new Proxy(sum, handler);
proxy(1, 2) // 6
proxy.call(null, 5, 6) // 22
proxy.apply(null, [7, 8]) // 30
複製代碼
以上,目標對象這裏是sum
,必須是一個函數,不然調用會報錯。每當proxy函數被直接調用或者call
,apply
方式調用都會當即觸發apply
方法,從而該調用被apply
方法攔截,這樣就能夠利用攔截修改返回值,若是不寫apply方法,默認調用sum方法,返回 結果。
含義:攔截new命令
target
:目標對象。
args
:構造函數的參數列表。
newTarget
:建立實例對象時,new命令做用的構造函數(下面例子的p)。
var p = new Proxy(function () {}, {
construct: function (target, args, newTarget) {
console.log('called: ' + args.join(', '));
return {
value: args[0] * 10
};
// return 1 //Uncaught TypeError: 'construct' on proxy: trap returned non-object ('1')
}
});
(new p(1)).value
// "called: 1"
// 10
複製代碼
以上,代理的目標對象必須是一個構造函數(只有構造函數纔可使用new操做符),當執行new p(1)
時,會馬上觸發construct
函數,也就是會被該函數攔截,這裏的返回值有講究,必須返回對象,不然會報錯。construct
方法執行完畢,p實例也就初始化完成了。
const target = {
foo: function () {
console.log(this === proxy);
}
};
const handler = {};
const proxy = new Proxy(target, handler);
target.foo() // false
proxy.foo() // true
複製代碼
能夠看出,當目標對象一旦被proxy
代理後,其內部this
就會指向proxy
,而非自身,所以須要注意這點。
好啦,今天關於Proxy的介紹就到這了,以上也只是針對幾個比較經常使用的操做函數進行了舉例說明,若是想了解更多,建議參考如下資料,如有錯誤,還望各位大神指正!
阮一峯:ECMAScript 6 入門-Proxy
阮一峯:ECMAScript 6 入門-Reflect
Proxy - JavaScript | MDN