代理
Reflect 对象
Reflect 对象时 ES6
引入的对象,通常与代理一起使用,通过代理可修改对象的默认行为,而 Reflect 对象则提供了一些与代理的处理器一一对应的方法,用于处理对象的默认行为。
Reflect 对象是内置对象,且没有构造函数,不能使用 new 离开创建实例,
Reflect 对象主要包括 13 个静态方法
方法名 | 说明 |
---|---|
apply | 对一个函数进行调用操作。 |
defineProperty |
为对象定义或修改一个属性,与 Object.defineProperty 的主要不同在于返回值,Object.defineProperty 返回的是调用它时的第一个参数,而 Reflect.defineProperty 返回的是布尔值,操作成功时返回 true,失败时返回 false。 |
deleteProperty |
删除对象的一个属性,操作类似 delete 操作符。在操作成功时返回 true,失败时返回 false。 |
get |
返回对象的属性值。 |
getOwnPropertyDescriptor |
返回指定的对象属性的属性描述符,如果属性不存在,返回 undefined。与 Object.getOwnPropertyDescriptor 的区别在于如何处理非对象目标。 |
getPrototypeOf |
返回对象的原型,与 Object.getPrototypeOf 的主要区别在于 Object.getPrototypeOf 会进行类型转换,再获取原型,而 Reflect.getPrototypeOf 不会。 |
has |
用于判断属性是否包含在对象内,与 in 操作符作用相同。 |
isExtensible |
用于判断一个对象是否可扩展,与 Object.isExtensible 的主要区别在于,当接收的参数不是一个对象时, Object.isExtensible 会返回 false,而 Reflect.isExtensible 则会抛出一个错误。 |
ownKeys |
返回由对象自身可枚举属性组成的数组。 |
preventExtensions |
将对象设置为不可扩展对象,与 Object.preventExtensions 的主要区别在于,无论传递给Object.preventExtensions 的参数是什么类型的值,都会返回参数值,而 Reflect.preventExtensions 在参数不是对象时会抛出错误。 |
set |
为对象的属性设置值。 |
setPrototypeOf |
为对象指定原型,与 Object.setPrototypeOf 的主要区别在于返回值, Object.setPrototypeOf 在失败时会抛出错误。而 Reflect.setPrototypeOf 在成功时返回 true,在失败时返回 false。 |
使用代理
let target = {};
console.log(target.y); // undefined
// 处理目标对象的对象,即代理对象
let handler = {
get: function(target, key) {
return key in target ? target[key] : 1;
}
}
let proxy = new Proxy(target, handler);
console.log(proxy.x); // 1
proxy.x = 2;
console.log(proxy.x); // 2
console.log(proxy.y); // 1
可代理的操作
getprototypeOf
操作
操作 getPrototypeOf
用于返回对象的原型,在执行以下 5 种操作时会触发该操作:
Object.getprototypeOf
Reflect.getprototypeOf
__proto__
Object.prototype.isPrototypeOf()
intanceof
setPrototypeOf
操作
操作 setPrototypeOf
可用来为对象设置一个原型,在执行 Object.setPrototypeOf
或 Reflect.setPrototypeOf
时会触发该操作。
如果不希望对象在使用时被修改原型对象,可以通过修改 setPrototypeOf
的默认行为,返回 false 或 抛出错误就能轻松实现。
let target = {};
console.log(target.y); // undefined
let handler = {
setPrototypeOf: function(target, newProto) {
return false;
// throw new Error('不能修改原型)
}
}
let proxy = new Proxy(target, handler);
Object.setPrototypeOf(proxy, Array); // 会抛出类型错误
isExtensible
操作
判断对象是否可以扩展,在调用 Object.isExtensible
或 Reflect.isExtensible
时会触发该操作。
修改这个操作的默认行为一定要小心,修改行为后的返回值必须与原对象在调用 Object.isExtensible
时返回的值相同,不然会报错。
let target = {};
let handler = {
isExtensible: function(target) {
return false;
}
}
let proxy = new Proxy(target, handler);
// 位置1
console.log(Object.isExtensible(target)); // true
console.log(Object.isExtensible(proxy)); // 抛出错误类型
以上代码中, Object.isExtensible(target)
返回的时 true,因为 Object.isExtentsible(proxy)
也必须返回 true,如果类似代码中那样返回 false,就会抛出类型错误。
如果在 位置1 ,添加 Object.preventExtensions(target);
将 target 修改为不可扩展的对象,代码就不会抛出类型错误,这时,如果将返回值修改为 true,又会抛出类型错误。
preventExtensions
操作
将一个对象转变为一个不可扩展的对象,也就是让对象不能添加新的成员。在调用 Object.preventExtensions(target)
或 Reflect.preventExtensible
时会触发。
getOwnPrototypeDescriptor
操作
用于返回对象自有属性的特性,在调用 Object.getOwnPrototypeDescriptor
或 Reflect.getOwnPrototypeDescriptor
时会触发该操作。
修改该操作的行为需要注意的是,如果返回结果不符合下列条件,就会抛出类型错误:
- 返回值不是对象或 undefined
- 如果属性是目标对象的不可配置属性(configurable 为 false),那么返回的属性特性与属性自身的特性不相符,例如以下代码:
let target = {};
Object.defineProperty(target, "x", {
enumerable: false,
configurable: false,
writable: false,
value: 1
})
let handler = {
getOwnPropertyDescriptor: function(target, property) {
return {
value: 1,
writable: false,
enumerable: false,
configurable: false
}
}
}
let proxy = new Proxy(target, handler);
console.log(Object.getOwnPropertyDescriptor(proxy, 'x')); // {value: 1, writable: false, enumerable: false, configurable: false}
以上代码为 target 定义了一个不可配置的属性 x,要在自定义的 getOwnPropertyDescriptor
中返回 x 的特性,就必须与 x 的自身特性相符,不然会报错。
const obj = {
x: 1
}
console.log(Object.getOwnPropertyDescriptor(obj, 'x')); // {value: 1, writable: true, enumerable: true, configurable: true}
console.log(Reflect.getOwnPropertyDescriptor(obj, 'x'));
defineProperty
操作
用于定于属性的特征,在调用 Object.defineProperty
和 Reflect.defineProperty
时会触发该操作。
- has 操作
用于判断对象是否还有某个属性,在执行以下操作的时候会触发:
- 属性查询:property in proxy
- 继承属性查询:
property in Object.create(proxy)
- with 检查:with(proxy) {(property )}
Relect.has()
通过重写 has 操作,可以帮助对象隐藏某些属性
let target = { x: 1 };
let handler = {
has: function(target, property) {
if (property === 'x') return false;
return true;
}
}
let proxy = new Proxy(target, handler);
console.log('x' in proxy); // false
- get 操作
返回属性的值,在执行以下操作时会触发
- 访问属性:proxy[‘property’] 和 proxy.property
- 访问原型链上的属性:
Object.create(proxy)[property]
Reflect.get()
在自定义该操作时,需要注意的是,当要访问的目标属性是不可写且不可配置的时候,返回值必须与属性值相同,不然会报错。
let target = {};
let handler = {
get: function(target, property, receiver) {
console.log(receiver); // proxy
if (!(property in receiver)) {
throw new TypeError("属性 " + property + " 不存在.")
}
return Reflect.get(target, property, receiver)
}
}
let proxy = new Proxy(target, handler);
console.log(proxy['x']); // false
- set 操作
用于设置属性值,在执行以下操作时触发
- 设置属性值:proxy[‘property’] = value 和
proxy.property = value
- 设置继承者的属性值:
Object.create(proxy)[property] = value
Reflect.set()
通过自定义 set 操作,可以实现赋值验证
let target = { x: 1 };
let handler = {
set: function(target, property, value) {
if (property === 'x' && isNaN(value)) {
throw new TypeError("属性 " + property + " 的值非法.")
}
return Reflect.set(target, property, value);
}
}
let proxy = new Proxy(target, handler);
proxy['x'] = 2;
console.log(proxy['x']); // 2
proxy['x'] = 'xxx'; // 报错
deleteProperty
操作
用于删除属性,在执行以下操作时触发
- 删除属性:delete proxy[property] 和 delete proxy.property
Reflect.deleteProperty()
通过自定义操作,可以避免属性被删除
let target = { x: 1, y: 2 };
let handler = {
deleteProperty: function(target, property) {
if (property === 'x') {
throw new TypeError("属性 " + property + " 不能删除")
}
return Reflect.deleteProperty(target, property)
}
}
let proxy = new Proxy(target, handler);
delete proxy['y'];
console.log(proxy); // Proxy {x: 1}
delete proxy['x'];
- ownKeys 操作
用于返回由对象自身可枚举属性组成的数组,在执行以下操作时触发
Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Object.keys()
Reflect.ownKeys()
在自定义的时候注意,返回的必须是数组。
通过自定义操作,可以把私有属性过滤掉
let target = { _x: 1, y: 2 };
let handler = {
ownKeys: function(target) {
return Reflect.ownKeys(target).filter(key => {
return typeof key !== 'string' || key[0] !== '_'
})
}
}
let proxy = new Proxy(target, handler);
console.log(Object.getOwnPropertyNames(target)); // ['_x', 'y']
console.log(Object.getOwnPropertyNames(proxy)); // ['y']
- apply 操作
用于调用函数,在执行以下操作时触发
- proxy(…args)
Function.prototype.apply()
和Function.prototype.call()
Reflect.apply()
在自定义的时候注意,目标对象必须是函数对象
let target = function(x, y, z) {
return true;
}
let handler = {
apply: function(target, thisArg, argumentsList) {
console.log(argumentsList); // [1, 2, 3]
return argumentsList[0] + argumentsList[1] + argumentsList[2];
}
}
let proxy = new Proxy(target, handler);
console.log(proxy(1, 2, 3)); // 6
- construct 操作
主要用于拦截 new 操作符,在使用 new 操作符创建实例或调用 Reflect.construct
方法时会触发该操作。
自定义的 construct 操作必须返回一个对象,不然会抛出一个类型错误。
可撤销的代理对象
通过代理的 revocable 方法创建一个可撤销的代理对象
let target = { x: 1 };
let proxy = Proxy.revocable(target, {});
console.log(proxy); // {proxy: Proxy, revoke: ƒ}
console.log(proxy.proxy.x); // 1
proxy.revoke()
console.log(proxy.proxy.x); // 抛出类型错误
调用 revocable 返回的是一个对象,在对象内包含代理对象,以及一个 revoke 方法,用于撤销代理对象。