const p = new Proxy(target, handler)
参数
target
要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
handler
一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。
其中 handler的写法要注意
handler 对象的方法
handler
对象是一个容纳一批特定属性的占位符对象。它包含有 Proxy
的各个捕获器(trap)。
所有的捕捉器是可选的。如果没有定义某个捕捉器,那么就会保留源对象的默认行为。
-
handler.getPrototypeOf()
Object.getPrototypeOf
方法的捕捉器。在读取代理对象的原型时触发该操作,比如在执行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
方法的捕捉器。在设置代理对象的原型时触发该操作,比如在执行Object.setPrototypeOf(proxy, null)
时。
/*
target
被拦截目标对象.
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
方法的捕捉器。在判断一个代理对象是否是可扩展时触发该操作,比如在执行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
方法的捕捉器。在让一个代理对象不可扩展时触发该操作,比如在执行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
方法的捕捉器。在获取代理对象某个属性的属性描述时触发该操作,比如在执行Object.getOwnPropertyDescriptor(proxy, "foo")
时。
/*
target
目标对象。
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。
* 如果属性作为目标对象的不可配置的属性存在,则该属性无法报告为不存在。
* 如果属性作为目标对象的属性存在,并且目标对象不可扩展,则该属性无法报告为不存在。
* 如果属性不存在作为目标对象的属性,并且目标对象不可扩展,则不能将其报告为存在。
* 属性不能被报告为不可配置,如果它不作为目标对象的自身属性存在,或者作为目标对象的可配置的属性存在。
* Object.getOwnPropertyDescriptor(target)的结果可以使用 Object.defineProperty 应用于目标对象,也不会抛出异常。
*/
-
handler.defineProperty()
Object.defineProperty
方法的捕捉器。在定义代理对象某个属性时的属性描述时触发该操作,比如在执行Object.defineProperty(proxy, "foo", {})
时。
/*
target
目标对象。
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"
/*
当调用 Object.defineProperty()` 或者 Reflect.defineProperty(),传递给 `defineProperty` 的 `descriptor` 有一个限制 - 只有以下属性才有用,非标准的属性将会被无视 :
* 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
* 如果目标对象不可扩展, 将不能添加属性。
* 不能添加或者修改一个属性为不可配置的,如果它不作为一个目标对象的不可配置的属性存在的话。
* 如果目标对象存在一个对应的可配置属性,这个属性可能不会是不可配置的。
* 如果一个属性在目标对象中存在对应的属性,那么 `Object.defineProperty(target, prop, descriptor)` 将不会抛出异常。
* 在严格模式下, `false` 作为` handler.defineProperty` 方法的返回值的话将会抛出 [`TypeError`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/TypeError "TypeError(类型错误) 对象用来表示值的类型非预期类型时发生的错误。") 异常.
*/
-
handler.has()
in
操作符的捕捉器。在判断代理对象是否拥有某个属性时触发该操作,比如在执行 "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
目标对象.
prop
需要检查是否存在的属性.
has 方法返回一个 boolean 属性的值.
*/
-
handler.get()
属性读取操作的捕捉器。在读取代理对象的某个属性时触发该操作,比如在执行 proxy.foo 时。
/*
以下是传递给get方法的参数,this上下文绑定在handler对象上.
target
目标对象。
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
/*
该方法会拦截目标对象的以下操作:
* 访问属性: proxy[foo]和proxy.bar
* 访问原型链上的属性: Object.create(proxy)[foo]
* `Reflect.get()
*/
-
handler.set()
属性设置操作的捕捉器。在给代理对象的某个属性赋值时触发该操作,比如在执行 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
* 目标对象。
property
* 将被设置的属性名或 Symbol.
value
* 新属性值.
receiver
* 最初被调用的对象。通常是 proxy 本身,但 handler 的 set 方法也有可能在原型链上,或以其他方式被间接地调用(因此不一定是 proxy 本身)
set() 方法应当返回一个布尔值
* 返回 `true` 代表属性设置成功.
* 在严格模式下,如果 `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
操作符的捕捉器。在删除代理对象的某个属性时触发该操作,即使用delete
运算符,比如在执行delete proxy.foo
时。
var p = new Proxy(target, {
deleteProperty: function(target, property) {
}
});
/*
target
* 目标对象
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
* 目标对象.
* 返回值
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()
函数调用操作的捕捉器。
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
* 目标对象(函数)。
thisArg
* 被调用时的上下文对象。
argumentsList
* 被调用时的参数数组。
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对象上生效,用于初始化代理的目标对象自身必须具有[[Construct]]内部方法(即 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
* 目标对象。
argumentsList
* constructor的参数列表。
newTarget
* 最初被调用的构造函数,就上面的例子而言是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