const p = new Proxy(target, handler)
參數(shù)
target
要使用 Proxy 包裝的目標(biāo)對象(可以是任何類型的對象粥鞋,包括原生數(shù)組缘挽,函數(shù),甚至另一個代理)呻粹。
handler
一個通常以函數(shù)作為屬性的對象到踏,各屬性中的函數(shù)分別定義了在執(zhí)行各種操作時代理 p 的行為。
其中 handler的寫法要注意
handler 對象的方法
handler
對象是一個容納一批特定屬性的占位符對象尚猿。它包含有 Proxy
的各個捕獲器(trap)。
所有的捕捉器是可選的楣富。如果沒有定義某個捕捉器凿掂,那么就會保留源對象的默認(rèn)行為。
-
handler.getPrototypeOf()
Object.getPrototypeOf
方法的捕捉器。在讀取代理對象的原型時觸發(fā)該操作庄萎,比如在執(zhí)行Object.getPrototypeOf(proxy)
時踪少。
const monster1 = {
eyeCount: 4
};
const monsterPrototype = {
eyeCount: 2
};
const handler = {
getPrototypeOf(target) {
return monsterPrototype;
}
};
const proxy1 = new Proxy(monster1, handler);
console.log(Object.getPrototypeOf(proxy1) === monsterPrototype);
// 輸出: true
console.log(Object.getPrototypeOf(proxy1).eyeCount);
// 輸出: 2
-
handler.setPrototypeOf()
Object.setPrototypeOf
方法的捕捉器。在設(shè)置代理對象的原型時觸發(fā)該操作糠涛,比如在執(zhí)行Object.setPrototypeOf(proxy, null)
時援奢。
/*
target
被攔截目標(biāo)對象.
prototype
對象新原型或為null
如果成功修改了[[Prototype]], setPrototypeOf 方法返回 true,否則返回 false.
*/
// 1. ------------------------------------------------------------------------------
var handlerReturnsFalse = {
setPrototypeOf(target, newProto) {
return false;
}
};
var newProto = {}, target = {};
var p1 = new Proxy(target, handlerReturnsFalse);
Object.setPrototypeOf(p1, newProto); // throws a TypeError
Reflect.setPrototypeOf(p1, newProto); // return false
// 2. ------------------------------------------------------------------------------
var handlerThrows = {
setPrototypeOf(target, newProto) {
throw new Error('custom error');
}
};
var newProto = {}, target = {};
var p2 = new Proxy(target, handlerThrows);
Object.setPrototypeOf(p2, newProto); // throws new Error("custom error")
Reflect.setPrototypeOf(p2, newProto); // throws new Error("custom error")
-
handler.isExtensible()
Object.isExtensible
方法的捕捉器。在判斷一個代理對象是否是可擴展時觸發(fā)該操作忍捡,比如在執(zhí)行Object.isExtensible(proxy)
時集漾。
const monster1 = {
canEvolve: true
};
const handler1 = {
isExtensible(target) {
return Reflect.isExtensible(target);
},
preventExtensions(target) {
target.canEvolve = false;
return Reflect.preventExtensions(target);
}
};
const proxy1 = new Proxy(monster1, handler1);
console.log(Object.isExtensible(proxy1));
// 輸出: true
console.log(monster1.canEvolve);
// 輸出: true
Object.preventExtensions(proxy1);
console.log(Object.isExtensible(proxy1));
// 輸出: false
console.log(monster1.canEvolve);
// 輸出: false
-
handler.preventExtensions()
Object.preventExtensions
方法的捕捉器。在讓一個代理對象不可擴展時觸發(fā)該操作砸脊,比如在執(zhí)行Object.preventExtensions(proxy)
時具篇。
const monster1 = {
canEvolve: true
};
const handler1 = {
preventExtensions(target) {
target.canEvolve = false;
Object.preventExtensions(target);
return true;
}
};
const proxy1 = new Proxy(monster1, handler1);
console.log(monster1.canEvolve);
// 輸出: true
Object.preventExtensions(proxy1);
console.log(monster1.canEvolve);
// 輸出: false
-
handler.getOwnPropertyDescriptor()
Object.getOwnPropertyDescriptor
方法的捕捉器。在獲取代理對象某個屬性的屬性描述時觸發(fā)該操作凌埂,比如在執(zhí)行Object.getOwnPropertyDescriptor(proxy, "foo")
時驱显。
/*
target
目標(biāo)對象。
prop
返回屬性名稱的描述瞳抓。
這個攔截器可以攔截這些操作:
* Object.getOwnPropertyDescriptor()
* Reflect.getOwnPropertyDescriptor()
*/
getOwnPropertyDescriptor 方法必須返回一個 object 或 undefined埃疫。
var p = new Proxy({ a: 20}, {
getOwnPropertyDescriptor: function(target, prop) {
console.log('called: ' + prop);
return { configurable: true, enumerable: true, value: 10 };
}
});
console.log(Object.getOwnPropertyDescriptor(p, 'a').value);
// "called: a"
// 10
/*
如果下列不變量被違反,代理將拋出一個 TypeError:
* getOwnPropertyDescriptor 必須返回一個 object 或 undefined孩哑。
* 如果屬性作為目標(biāo)對象的不可配置的屬性存在栓霜,則該屬性無法報告為不存在。
* 如果屬性作為目標(biāo)對象的屬性存在臭笆,并且目標(biāo)對象不可擴展叙淌,則該屬性無法報告為不存在。
* 如果屬性不存在作為目標(biāo)對象的屬性愁铺,并且目標(biāo)對象不可擴展鹰霍,則不能將其報告為存在。
* 屬性不能被報告為不可配置茵乱,如果它不作為目標(biāo)對象的自身屬性存在茂洒,或者作為目標(biāo)對象的可配置的屬性存在。
* Object.getOwnPropertyDescriptor(target)的結(jié)果可以使用 Object.defineProperty 應(yīng)用于目標(biāo)對象瓶竭,也不會拋出異常督勺。
*/
-
handler.defineProperty()
Object.defineProperty
方法的捕捉器。在定義代理對象某個屬性時的屬性描述時觸發(fā)該操作斤贰,比如在執(zhí)行Object.defineProperty(proxy, "foo", {})
時智哀。
/*
target
目標(biāo)對象。
property
待檢索其描述的屬性名荧恍。
descriptor
待定義或修改的屬性的描述符瓷叫。
defineProperty 方法必須以一個 Boolean返回屯吊,表示定義該屬性的操作成功與否。
*/
var p = new Proxy({}, {
defineProperty: function(target, prop, descriptor) {
console.log('called: ' + prop);
return true;
}
});
var desc = { configurable: true, enumerable: true, value: 10 };
Object.defineProperty(p, 'a', desc);
// "called: a"
/*
當(dāng)調(diào)用 Object.defineProperty()` 或者 Reflect.defineProperty()摹菠,傳遞給 `defineProperty` 的 `descriptor` 有一個限制 - 只有以下屬性才有用盒卸,非標(biāo)準(zhǔn)的屬性將會被無視 :
* enumerable
* configurable
* writable
* value
* get
* set
*/
var p = new Proxy({}, {
defineProperty(target, prop, descriptor) {
console.log(descriptor);
return Reflect.defineProperty(target, prop, descriptor);
}
});
Object.defineProperty(p, 'name', {
value: 'proxy',
type: 'custom'
});
// { value: 'proxy' }
/*
如果違背了以下的不變量,proxy會拋出 TypeError
* 如果目標(biāo)對象不可擴展次氨, 將不能添加屬性蔽介。
* 不能添加或者修改一個屬性為不可配置的,如果它不作為一個目標(biāo)對象的不可配置的屬性存在的話煮寡。
* 如果目標(biāo)對象存在一個對應(yīng)的可配置屬性虹蓄,這個屬性可能不會是不可配置的。
* 如果一個屬性在目標(biāo)對象中存在對應(yīng)的屬性洲押,那么 `Object.defineProperty(target, prop, descriptor)` 將不會拋出異常武花。
* 在嚴(yán)格模式下, `false` 作為` handler.defineProperty` 方法的返回值的話將會拋出 [`TypeError`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/TypeError "TypeError(類型錯誤) 對象用來表示值的類型非預(yù)期類型時發(fā)生的錯誤杈帐。") 異常.
*/
-
handler.has()
in
操作符的捕捉器体箕。在判斷代理對象是否擁有某個屬性時觸發(fā)該操作,比如在執(zhí)行 "foo"in
proxy 時挑童。
const handler1 = {
has(target, key) {
if (key[0] === '_') {
return false;
}
return key in target;
}
};
const monster1 = {
_secret: 'easily scared',
eyeCount: 4
};
const proxy1 = new Proxy(monster1, handler1);
console.log('eyeCount' in proxy1);
// 輸出: true
console.log('_secret' in proxy1);
// 輸出: false
console.log('_secret' in monster1);
//輸出: true
/*
target
目標(biāo)對象.
prop
需要檢查是否存在的屬性.
has 方法返回一個 boolean 屬性的值.
*/
-
handler.get()
屬性讀取操作的捕捉器累铅。在讀取代理對象的某個屬性時觸發(fā)該操作,比如在執(zhí)行 proxy.foo 時站叼。
/*
以下是傳遞給get方法的參數(shù)娃兽,this上下文綁定在handler對象上.
target
目標(biāo)對象。
property
被獲取的屬性名尽楔。
receiver
Proxy或者繼承Proxy的對象
返回值
get方法可以返回任何值投储。
*/
var p = new Proxy({}, {
get: function(target, prop, receiver) {
console.log("called: " + prop);
return 10;
}
});
console.log(p.a);
// "called: a"
// 10
/*
該方法會攔截目標(biāo)對象的以下操作:
* 訪問屬性: proxy[foo]和proxy.bar
* 訪問原型鏈上的屬性: Object.create(proxy)[foo]
* `Reflect.get()
*/
-
handler.set()
屬性設(shè)置操作的捕捉器。在給代理對象的某個屬性賦值時觸發(fā)該操作阔馋,比如在執(zhí)行 proxy.foo = 1 時玛荞。
function Monster() {
this.eyeCount = 4;
}
const handler1 = {
set(obj, prop, value) {
if ((prop === 'eyeCount') && ((value % 2) !== 0)) {
console.log('Monsters must have an even number of eyes');
} else {
return Reflect.set(...arguments);
}
}
};
const monster1 = new Monster();
const proxy1 = new Proxy(monster1, handler1);
proxy1.eyeCount = 1;
// 輸出: "Monsters must have an even number of eyes"
console.log(proxy1.eyeCount);
// 輸出: 4
/*
target
* 目標(biāo)對象。
property
* 將被設(shè)置的屬性名或 Symbol.
value
* 新屬性值.
receiver
* 最初被調(diào)用的對象呕寝。通常是 proxy 本身勋眯,但 handler 的 set 方法也有可能在原型鏈上,或以其他方式被間接地調(diào)用(因此不一定是 proxy 本身)
set() 方法應(yīng)當(dāng)返回一個布爾值
* 返回 `true` 代表屬性設(shè)置成功.
* 在嚴(yán)格模式下下梢,如果 `set()` 方法返回 `false`客蹋,那么會拋出一個 TypeError異常.
*/
var p = new Proxy({}, {
set: function(target, prop, value, receiver) {
target[prop] = value;
console.log('property set: ' + prop + ' = ' + value);
return true;
}
})
console.log('a' in p); // false
p.a = 10; // "property set: a = 10"
console.log('a' in p); // true
console.log(p.a); // 10
-
handler.deleteProperty()
delete
操作符的捕捉器。在刪除代理對象的某個屬性時觸發(fā)該操作孽江,即使用delete
運算符讶坯,比如在執(zhí)行delete proxy.foo
時。
var p = new Proxy(target, {
deleteProperty: function(target, property) {
}
});
/*
target
* 目標(biāo)對象
property
* 待刪除的屬性名
deleteProperty 必須返回一個 Boolean 類型的值岗屏,表示了該屬性是否被成功刪除闽巩。
*/
var p = new Proxy({}, {
deleteProperty: function(target, prop) {
console.log('called: ' + prop);
return true;
}
});
delete p.a; // "called: a"
-
handler.ownKeys()
Object.getOwnPropertyNames
方法和Object.getOwnPropertySymbols
方法的捕捉器钧舌。
const monster1 = {
_age: 111,
[Symbol('secret')]: 'I am scared!',
eyeCount: 4
};
const handler1 = {
ownKeys(target) {
return Reflect.ownKeys(target);
}
};
const proxy1 = new Proxy(monster1, handler1);
for (const key of Object.keys(proxy1)) {
console.log(key);
// 輸出: "_age"
// 輸出: "eyeCount"
}
/*
target
* 目標(biāo)對象.
* 返回值
ownKeys 方法必須返回一個可枚舉對象.
*/
var p = new Proxy({}, {
ownKeys: function(target) {
console.log('called');
return ['a', 'b', 'c'];
}
});
console.log(Object.getOwnPropertyNames(p));
// "called"
// [ 'a', 'b', 'c' ]
-
handler.apply()
函數(shù)調(diào)用操作的捕捉器。
function sum(a, b) {
return a + b;
}
const handler = {
apply: function(target, thisArg, argumentsList) {
console.log(`Calculate sum: ${argumentsList}`);
// expected output: "Calculate sum: 1,2"
return target(argumentsList[0], argumentsList[1]) * 10;
}
};
const proxy1 = new Proxy(sum, handler);
console.log(sum(1, 2));
// 輸出: 3
console.log(proxy1(1, 2));
// 輸出: 30
/*
target
* 目標(biāo)對象(函數(shù))涎跨。
thisArg
* 被調(diào)用時的上下文對象。
argumentsList
* 被調(diào)用時的參數(shù)數(shù)組崭歧。
apply方法可以返回任何值隅很。
*/
var p = new Proxy(function() {}, {
apply: function(target, thisArg, argumentsList) {
console.log('called: ' + argumentsList.join(', '));
return argumentsList[0] + argumentsList[1] + argumentsList[2];
}
});
console.log(p(1, 2, 3));
// "called: 1, 2, 3"
// 6
-
handler.construct()
new
操作符的捕捉器。
/*
handler.construct() 方法用于攔截new操作符. 為了使new操作符在生成的Proxy對象上生效率碾,用于初始化代理的目標(biāo)對象自身必須具有[[Construct]]內(nèi)部方法(即 new target 必須是有效的)叔营。
*/
function monster1(disposition) {
this.disposition = disposition;
}
const handler1 = {
construct(target, args) {
console.log('monster1 constructor called');
// expected output: "monster1 constructor called"
return new target(...args);
}
};
const proxy1 = new Proxy(monster1, handler1);
console.log(new proxy1('fierce').disposition);
// 輸出: "fierce"
/*
target
* 目標(biāo)對象。
argumentsList
* constructor的參數(shù)列表所宰。
newTarget
* 最初被調(diào)用的構(gòu)造函數(shù)绒尊,就上面的例子而言是p。
construct 方法必須返回一個對象仔粥。
*/
var p = new Proxy(function() {}, {
construct: function(target, argumentsList, newTarget) {
console.log('called: ' + argumentsList.join(', '));
return { value: argumentsList[0] * 10 };
}
});
console.log(new p(1).value);
// "called: 1"
// 10