Proxy
构造函数
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
语法:new Proxy(target, handler)
参数:
- target:要使用
Proxy
包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理) - handler:一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理
p
的行为
方法
Proxy.revocable()
Proxy.revocable()
方法可以用来创建一个可撤销的代理对象。
语法:Proxy.revocable(target, handler)
target
:将用Proxy
封装的目标对象。可以是任何类型的对象,包括原生数组,函数,甚至可以是另外一个代理对象。handler
:一个对象,其属性是一批可选的函数,这些函数定义了对应的操作被执行时代理的行为。
handler 对象的方法
handler
对象是一个容纳一批特定属性的占位符对象。它包含有 Proxy
的各个捕获器(trap)。
所有的捕捉器是可选的。如果没有定义某个捕捉器,那么就会保留源对象的默认行为。
handler.get()
handler.get()
方法用于拦截对象的读取属性操作。
语法:
new Proxy(target, {get(target, property, receiver) {},
});
参数:
target
:目标对象property
:被获取的属性名receiver
:Proxy 或者继承 Proxy 的对象
返回:返回任何值
该方法会拦截目标对象的以下操作:
- 访问属性:
proxy[foo] 和
proxy.bar
- 访问原型链上的属性:
Object.create(proxy)[foo]
Reflect.get()
如果违背了以下的约束,proxy 会抛出 TypeError
:
- 如果要访问的目标属性是不可写以及不可配置的,则返回的值必须与该目标属性的值相同
- 如果要访问的目标属性没有配置访问方法,即 get 方法是 undefined 的,则返回值必须为 undefined
handler.set()
handler.set()
方法是设置属性值操作的捕获器。
语法:
new Proxy(target, {set(target, property, value, receiver) {}
});
参数:
target
:目标对象property
:被获取的属性名- value:新属性值
receiver
:Proxy 或者继承 Proxy 的对象
返回:返回一个布尔值
- 返回
true
代表属性设置成功 - 在严格模式下,如果
set()
方法返回false
,那么会抛出一个TypeError
异常
该方法会拦截目标对象的以下操作:
- 指定属性值:
proxy[foo] = bar
和proxy.foo = bar
- 指定继承者的属性值:
Object.create(proxy)[foo] = bar
Reflect.set()
如果违背以下的约束条件,proxy 会抛出一个 TypeError
异常:
- 若目标属性是一个不可写及不可配置的数据属性,则不能改变它的值
- 如果目标属性没有配置存储方法,即
[[Set]]
属性的是undefined
,则不能设置它的值 - 在严格模式下,如果
set()
方法返回false
,那么也会抛出一个TypeError
异常
handler.has()
handler.has()
方法是针对 in
操作符的代理方法。
语法:
new Proxy(target, {has(target, prop) {},
});
参数:
target
:目标对象prop
:需要检查是否存在的属性
返回:返回一个 boolean 属性的值
这个钩子可以拦截下面这些操作:
- 属性查询:
foo in proxy
- 继承属性查询:
foo in Object.create(proxy)
with
检查: with(proxy) { (foo); }
Reflect.has()
如果违反了下面这些规则,proxy 将会抛出 TypeError
:
- 如果目标对象的某一属性本身不可被配置,则该属性不能够被代理隐藏。
- 如果目标对象为不可扩展对象,则该对象的属性不能够被代理隐藏
handler.apply()
handler.apply()
方法用于拦截函数的调用。
语法:
new Proxy(target, {apply(target, thisArg, argumentsList) {},
});
参数:
target
:目标对象(函数)thisArg
:被调用时的上下文对象argumentsList
:被调用时的参数数组
返回:返回任何值
该方法会拦截目标对象的以下操作:
proxy(...args)
Function.prototype.apply()
和Function.prototype.call()
Reflect.apply()
handler.construct()
handler.construct()
方法用于拦截 new
操作符。为了使 new 操作符在生成的 Proxy 对象上生效,用于初始化代理的目标对象自身必须具有 [[Construct]] 内部方法(即 new target
必须是有效的)。
语法:
new Proxy(target, {construct(target, argumentsList, newTarget) {},
});
参数:
target
:目标对象argumentsList
:constructor 的参数列表newTarget
:最初被调用的构造函数,就上面的例子而言是 p
返回:返回一个对象
该拦截器可以拦截以下操作:
new proxy(...args)
Reflect.construct()
如果违反以下约定,代理将会抛出错误 TypeError
:
- 必须返回一个对象
handler.deleteProperty()
handler.deleteProperty()
方法用于拦截对对象属性的 delete
操作。
语法:
new Proxy(target, {deleteProperty(target, property) {},
});
参数:
target
:目标对象property
:被获取的属性名
返回:返回一个 Boolean
类型的值,表示了该属性是否被成功删除
该方法会拦截以下操作:
- 删除属性:
delete proxy[foo]
和delete proxy.foo
Reflect.deleteProperty()
如果违背了以下不变量,proxy 将会抛出一个 TypeError
:
- 如果目标对象的属性是不可配置的,那么该属性不能被删除。
handler.ownKeys()
handler.ownKeys()
方法用于拦截 Reflect.ownKeys()
。
语法:
new Proxy(target, {ownKeys(target) {},
});
参数:
target
:目标对象
返回:返回一个可枚举对象
该拦截器可以拦截以下操作::
Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Object.keys()
Reflect.ownKeys()
如果违反了下面的约束,proxy 将抛出错误 TypeError
:
ownKeys
的结果必须是一个数组- 数组的元素类型要么是一个
String
,要么是一个Symbol
- 结果列表必须包含目标对象的所有不可配置(non-configurable)、自有(own)属性的 key
- 如果目标对象不可扩展,那么结果列表必须包含目标对象的所有自有(own)属性的 key,不能有其他值
handler.getPrototypeOf()
handler.getPrototypeOf()
是一个代理(Proxy)方法,当读取代理对象的原型时,该方法就会被调用。
语法:
new Proxy(target, {getPrototypeOf(target) {}
})
参数:
- target:被代理的目标对象。
返回:返回一个对象或者 null
。
这个方法可以拦截以下操作:
Object.getPrototypeOf()
Reflect.getPrototypeOf()
Object.prototype.__proto__
Object.prototype.isPrototypeOf()
instanceof
如果遇到了下面情况,JS 引擎会抛出 TypeError
异常:
getPrototypeOf()
方法返回的不是对象也不是null
- 目标对象是不可扩展的,且
getPrototypeOf()
方法返回的原型不是目标对象本身的原型
handler.setPrototypeOf()
handler.setPrototypeOf()
方法主要用来拦截 Object.setPrototypeOf()
.
语法:
new Proxy(target, {setPrototypeOf(target, prototype) {}
})
参数:
target
:被拦截目标对象prototype
:对象新原型或为null
返回:如果成功修改了[[Prototype]]
,返回 true
,否则返回 false
这个方法可以拦截以下操作:
Object.setPrototypeOf()
Reflect.setPrototypeOf()
如果遇到了下面情况,JS 引擎会抛出 TypeError
异常:
- 如果
target
不可扩展,原型参数必须与Object.getPrototypeOf(target)
的值相同
handler.isExtensible()
handler.isExtensible()
方法用于拦截对对象的 Object.isExtensible()
。
语法:
new Proxy(target, {isExtensible(target) {}
})
参数:
target
:目标对象
返回:返回一个 Boolean 值或可转换成 Boolean 的值
这个方法可以拦截以下操作:
Object.isExtensible()
Reflect.isExtensible()
如果遇到了下面情况,JS 引擎会抛出 TypeError
异常:
Object.isExtensible(proxy)
必须同Object.isExtensible(target)
返回相同值
handler.preventExtensions()
handler.preventExtensions()
方法用于设置对 Object.preventExtensions()
的拦截。
语法:
new Proxy(target, {preventExtensions(target) {}
})
参数:
target
:所要拦截的目标对象
返回:返回一个布尔值
这个方法可以拦截以下操作:
Object.preventExtensions()
Reflect.preventExtensions()
如果遇到了下面情况,JS 引擎会抛出 TypeError
异常:
- 如果目标对象是可扩展的,那么只能返回 false
handler.getOwnPropertyDescriptor()
handler.getOwnPropertyDescriptor()
方法是 Object.getOwnPropertyDescriptor()
的钩子。
语法:
new Proxy(target, {getOwnPropertyDescriptor(target, prop) {}
})
参数:
target
:所要拦截的目标对象- prop:返回属性名称的描述
返回:返回一个 object 或 undefined
这个方法可以拦截以下操作:
Object.getOwnPropertyDescriptor()
Reflect.getOwnPropertyDescriptor()
如果下列不变量被违反,代理将抛出一个 TypeError
:
getOwnPropertyDescriptor
必须返回一个 object 或undefined
。- 如果属性作为目标对象的不可配置的属性存在,则该属性无法报告为不存在。
- 如果属性作为目标对象的属性存在,并且目标对象不可扩展,则该属性无法报告为不存在。
- 如果属性不存在作为目标对象的属性,并且目标对象不可扩展,则不能将其报告为存在。
- 属性不能被报告为不可配置,如果它不作为目标对象的自身属性存在,或者作为目标对象的可配置的属性存在。
- Object.getOwnPropertyDescriptor(target)的结果可以使用 Object.defineProperty 应用于目标对象,也不会抛出异常。
handler.defineProperty()
handler.defineProperty()
用于拦截对象的 Object.defineProperty()
操作。
语法:
new Proxy(target, {defineProperty(target, property, descriptor) {}
})
参数:
target
:目标对象property
:待检索其描述的属性名descriptor
:待定义或修改的属性的描述符
返回:返回 Boolean
,表示定义该属性的操作成功与否
该方法会拦截目标对象的以下操作:
Object.defineProperty()
Reflect.defineProperty()
proxy.property='value'
如果违背了以下的不变量,proxy 会抛出 TypeError
:
- 如果目标对象不可扩展,将不能添加属性
- 不能添加或者修改一个属性为不可配置的,如果它不作为一个目标对象的不可配置的属性存在的话
- 如果目标对象存在一个对应的可配置属性,这个属性可能不会是不可配置的
- 如果一个属性在目标对象中存在对应的属性,那么
Object.defineProperty(target, prop, descriptor)
将不会抛出异常 - 在严格模式下,
false
作为handler.defineProperty
方法的返回值的话将会抛出TypeError
异常
Reflect
Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与 proxy.handler 的方法相同。
Reflect
不是一个函数对象,因此它是不可构造的。