Proxy
Proxy对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)
语法
/*
* 参数
* target 要使用Proxy包装的目标对象(可以是任何类型的对象、包括原生数组,函数,甚至另一个代理)
* handler 一个通常以函数作为属性的对象,各属性中的函数分别定义了再执行各种操作时代理p的行为
*/
const p = new Proxy(target,handler)
方法
Proxy.revocable
/*
* 创建一个可撤销的Proxy对象
* target 将用Proxy封装的目标对象,可以是任何类型的对象,包括原生数组、函数、
* 甚至可以是另外一个代理对象
* handler 一个对象,其属性是一批可选的函数,这些函数定义了对应的操作被执行时代理的行为
*/
const {"proxy":proxy,"revoke":revoke}= Proxy.revocable(target,handle)
/*
* 返回值是一个包含了代理对象本身和它的撤销方法的可撤销Proxy对象
* proxy 表示新生成的代理对象本身和用一般方法new Proxy(target,handler)创建的代理对象一样,除了它
* 可以被撤销
* revoke 撤销方法,调用的时候不需要加任何参数,就可以撤销掉生成的代理对象
*/
/*
* 一旦某个代理对象被注销,将变得几乎完全不可调用,执行任何的可代理操作都会抛出TypeError移仓
* (注意:执行可代理操作以外的操作不会抛出异常)
* 一旦被撤销,这个代理对象便不可能被直接恢复到原来的状态,同时和它关联的目标对象以及处理器对象都有可能
* 被垃圾回收掉。再次调用撤销方法revoke()则不会有任何效果,但也不能保存
*/
handler对象的方法
handler
对象是一个容纳一批特定属性的占位符对象,包含有Proxy
的各个捕获器(trap)
所有的捕捉器都是可选的。如果没有定义某个捕捉器,那么就会保留源对象的默认行为
handler.getPrototypeOf()
handler.getPrototypeOf()=>Object.getPrototypeOf方法的拦截器
/*
* handler.getPrototypeOf()是一个代理(Proxy)方法,当读取代理对象的原型时,该方法就会被调用
*
*/
const proxy1 = new Proxy({eyeCount:2},{
getPrototypeOf(target) {
return {eyeCount:4}
}
})
console.log(Object.getPrototypeOf(proxy1).eyeCount) //4
语法
/*
* 当getPrototypeOf方法被调用时,this指向的是它所属的处理器对象
* target 被代理的目标对象
*/
const p = new Proxy(obj,{
getPrototypeOf(target) {
return Object || null
}
})
/*
* getPrototypeOf方法的返回值必须是一个对象或者null
*/
触发情况
在JavaScript中,下面这五种操作(方法/属性/运算符)可以触发JS引擎读取一个对象的原型,也就是可以触发getPrototypeOf()代理方法的运行
1.Object.getPrototypeOf()
2.Reflect.getPrototypeOf()
3.__proto__
4.Object.prototype.isPrototypeOf(target) //判断对象是否在对象实例的原型链上
5.instanceof
如果遇到了下面两种情况,JS引擎会抛出TypeError异常
1.getPrototypeOf()方法返回的不是对象也不是null
2.目标对象是不可扩展的,且getPrototypeOf()方法返回的原型不是目标对象本身的原型
handler.setPrototypeOf()
handler.setPrototypeOf() => Object.setPrototypeOf()的拦截器
语法
/*
* target 被拦截目标对象
* prototype 对象新原型或者是null
*/
var p = new Proxy(target,{
setPrototypeOf:function(target,prototype){}
})
/*
* 返回值
* 如果成功修改了[[prototype]],setPrototypeOf方法返回true,否则返回了false
*/
触发情况
这个方法可以拦截以下操作
1.Object.setPrototypeOf()
2.Reflect.setPrototypeOf()
如果违反了下列规则,则proxy将抛出一个TypeError
- 如果target不可扩展,原型参数必须与Object.getPrototypeOf(target)的值相同
handler.isExtensible()
handler.isExtensible()方法用于拦截对对象的Object.isExtensible()(判断目标对象是否可以扩展)
语法
/*
* target 目标对象
*/
var p = new Proxy(target,{
isExtensible:function(target) {
}
})
/*
* 返回值 isExtensible方法必须返回一个Boolean值或可转换成Boolean的值
*/
触发场景
该方法会拦截目标对象的以下操作
1.Object.isExtensible()
2.Reflect.isExtensible()
如果违背了以下的约束,proxy会抛出TypeError
- Object.isExtensible(proxy)必须同Object.isExtensible(target)返回相同值。
handler.preventExtensions()
handler.preventExtensions()方法用于设置对Object.preventExtensions()(设置对象不可扩展)的拦截
语法
/*
* target 所要拦截的目标对象
*/
var p =new Proxy(target,{
preventExtensions:function(target){}
})
/*
* 返回值 preventExtensions 方法返回一个布尔值
*/
触发情况
这个方法拦截以下操作
1.Object.preventExtensions()
2.Reflect.preventExtensions()
如果违反了下列规则,proxy则会抛出一个TypeError
- 如果目标是可扩展的,那么只能返回false
handler.getOwnPropertyDescriptor()
handler.getOwnPropertyDescriptor()方法是Object.getOwnPropertyDescriptor()
的钩子
语法
/*
* 参数
* target 目标对象
* prop 返回属性名称的描述
*/
var p = new Proxy(target,{
getOwnPropertyDescriptor:function(target,prop){}
})
/*
* 返回值
* getOwnPropertyDescriptor 方法必须返回一个object或undefined
*/
触发情况
方法可以拦截这些操作
Object.getOwnPropertyDescriptor()
Reflect.getOwnPropertyDescriptor()
如果下列不变量被违反,代理将抛出一个TypeError
- getOwnPropertyDescriptor 必须返回一个object或undefined
- 如果属性作为目标对象的不可配置的属性存在,则该属性无法报告为不存在
- 如果属性作为目标对象的属性存在,并且目标对象不可扩展,则该属性无法报告为不存在
- 如果属性不存在作为目标对象的属性,并且目标对象不可扩展,则不能将其报告为存在
- 属性不能被报告为不可配置,如果它不作为目标对象的自身属性存在,或者作为目标对象的可配置的属性存在
- Object.getOwnPropertyDescriptor(target)的结果可以使用Object.defineProperty应用于目标对象,也不会抛出异常
Ojbect.getOwnPropertyDescriptor()
Object.getOwnPropertyDescriptor()方法返回指定对象上一个自由属性对应的属性描述符。(自由属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
/*
* 参数
* obj 需要查找的目标对象
* prop 目标对象内属性名称
*/
Object.getOwnPropertyDescriptor(obj,prop)
/*
* 返回值
* 如果指定的属性存在于对象上,则返回其属性描述符对象,否则返回undefined
* {configurable:true,enumerable:true,get:function,set:function}
*/
handler.defineProperty()
handler.defineProperty()
用于拦截对象的Object.defineProperty()操作
语法
/*
* 参数
* target 目标对象
* property 待检索其描述的属性名
* descriptor 待定义或修改的属性描述符,只支持这些属性
* configurable\enumerable\writable\value\get\set
*/
var p = new Proxy(target,{
defineProperty:function(target,property,descriptor) {
return true || false;
}
})
/*
* 返回值
* defineProperty 方法必须以一个Boolean返回,表示定义该属性的操作是否成功
*/
触发情况
方法会拦截目标对象的以下操作
Object.defineProperty()
Reflect.defineProperty()
proxy.property='value'
如果违反了以下的不变量,proxy会抛出TypeError
- 如果目标对象不可扩展,将不能添加属性
- 不能添加或修改一个属性为不可配置的,如果它不作为一个目标对象的不可配置的属性存在的话
- 如果目标对象存在一个对应的可配置属性,这个属性可能不会是不可配置的
- 如果一个属性在目标对象中存在对应的属性,那么Object.defineProperty(target,prop,descriptor)将不会抛出异常
- 在严格模式下,false作为handler.definerProperty方法的返回值的话将会抛出TypeError异常
Object.defineProperty()
Object.defineProperty()
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象
应当直接在Object构造器对象上调用此方法,而不是在任意一个Object类型的实例上调用
//语法
Object.defineProperty(obj,prop,descriptor)
/*
* 参数
* obj 要定义属性的对象
* prop 要定义或修改的属性的名称或Symbol
* descriptor 要定义或修改的属性描述符
*/
/*
* 返回值
* 被传递给函数的对象
*/
handler,has()
handler.has()
方法是针对in
操作符的代理方法
语法
/*
* 参数
* target 目标对象
* prop 需要检查是否存在的属性
*/
var p = new Proxy(target,{
has:function(target,prop) {
return true || false
}
})
/*
* 返回值
* has方法返回一个Boolean属性的值
*/
触发情况
方法可以拦截下面这些操作
- 属性查询:foo in proxy
- 继承属性查询 foo in Object.create(proxy)
- with检查:with(proxy){(foo);}
- Reflect.has()
如果违反了下面这些规则,proxy将会抛出TypeError
- 如果目标对象的某一属性本身不可被配置,则该属性不能够被代理隐藏
- 如果目标对象为不可扩展对象,则该对象的属性不能够被代理隐藏
handler.get()
handler.get()
方法用于拦截对象的读取属性操作
语法
/*
* 参数
* target 目标对象
* property 被获取的属性名
* receiver Proxy或者继承Proxy的对象
*/
var p = new Proxy(target,{
get:function(target,property,receiver) {
}
})
/*
* 返回值
* get方法可以返回任何值
*/
触发情况
方法会拦截目标对象的以下操作
- 访问属性:proxy[foo]和proxy.bar
- 访问原型链上的属性:Object.create(proxy)[foo]
- Reflect.get()
如果违背了以下的约束,proxy会抛出TypeError
- 如果要访问的目标属性是不可写以及不可配置的,则返回的值必须与该目标属性的值相同
- 如果要访问的目标属性没有配置访问方法,即get方法是undefined的,则返回值必须为undefined
handler.set()
handler.set()
方法是设置属性值操作的捕获器
语法
/*
* 参数
* target 目标对象
* property 将被设置的属性名或Symbol
* value 新属性值
* receiver 最初被调用的对象。通常是proxy本身,但handler的set方法也有可能在原型链上,或以其他方法被间接地调用
*/
const p = new Proxy(target,{
set:function(target,proerty,value,receiver) {
}
})
/*
* 返回值
* set()方法应当返回一个布尔值
* 返回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.deleteProperty()```
handler.deleteProperty()
方法用于拦截对对象属性的delete操作
语法
/*
* 参数
* target 目标对象
* property 待删除的属性名
*/
var p =new Proxy(target,{
deleteProperty:function(target,property){
return true || false;
}
})
/*
* 返回值
* deleteProperty 必须返回一个Boolean类型的值,表示该属性是否被成功删除
*/
触发情况
方法会拦截以下操作
- 删除属性:delete proxy[foo] 和 delete proxy.foo
- Reflect.deleteProperty()
如果违背了以下不变量,proxy将会抛出一个TypeError
- 如果目标对象的属性是不可配置的,那么该属性不能被删除
handler.ownKeys()
handler.ownKeys
方法用于拦截获取对象的属性名
语法
/*
* 参数
* target 目标对象
*/
var p = new Proxy(target,{
ownKeys:function(target) {
return Object
}
})
/*
* 返回值 ownKeys方法必须返回一个可枚举对象
*/
触发情况
方法可以拦截以下操作
- Object.getOwnPropertyNames()
- Object.getOwnPropertySymbols()
- Object.keys()
- Reflect.ownKeys()
如果违反了下面的约束,proxy将会抛出错误TypeError
- ownKeys的结果必须是一个数组
- 数组的元素类型要么是一个String,要么是一个Symbol
- 结果列表必须包含目标对象的所有不可配置、自有属性的key
- 如果目标对象不可扩展,那么结果列表必须包含目标对象的所有自有属性的key,不能有其它值
handler.apply()
handler.apply()
方法用于拦截函数的调用
/*
* 参数
* target 目标对象(函数)
* thisArg 被调用时的上下文对象
* argumentsList 被调用时的参数数组
*/
var p = new Proxy(target,{
apply:function(target,thisArg,argumentsList) {
}
})
/*
* 返回值
* apply 方法可以返回任何值
*/
触发情况
方法会拦截目标对象的以下操作
- proxy(…args)
- Function.prototype.apply()和Function.prototype.call()
- Reflect.apply()
如果违反了以下约束,代理将会抛出一个TypeError
target
必须是可被调用的,也就是说,它必须是一个函数对象
handler.construct()
handler.construct()
方法用于拦截new操作符,为了是new操作符在生成的Proxy对象上生效,用于初始化代理的目标对象自身必须具有[[Construct]]内部方法(即 new target 必须是有效的)
语法
/*
* 参数
* target 目标对象
* argumetnsList constructor的参数列表
* newTarget 最初被调用的构造函数
*/
var p =new Proxy(target,{
construct:function(target,argumentsList,newTarget){
return Object
}
})
/*
* 返回值 construct方法必须返回一个对象
*/
触发情况
方法可以拦截以下操作
- new proxy(…args)
- Reflect.construct()
如果返回值不是一个对象,代理将会抛出错误TypeError
如果target不具有一个有效的constructor,也会报错