JavaScript学习笔记7

Proxy和Reflect

proxy(代理):一个Proxy对象包装另一个对象并拦截诸如读取/写入属性和其他操作,可以选择自行处理它们。或者透明的允许该对象处理它们。

let proxy = new Proxy(target,handler)
target – 是要包装的对象,可以是任何东西,包括函数。
handlert – 代理配置:带有“捕捉器”(有拦截操作的方法)的对象,比如get捕捉器用于读取target的属性,set捕捉器用于写入target的属性。
对proxy进行操作,如果handler中存在相应的捕捉器,则将它运行,并且Proxy有机会对其进行处理,否则将直接对target进行处理,所有对proxy的操作都直接转发给了target。

如果没有handler捕捉器,写入操作proxy.test=会将值写入target,读取会从target返回对应的值,迭代proxy会从target返回对应的值。proxy是一个target的透明包装器。要激活更多的功能,则需要我们添加捕捉器,用它进行拦截操作。

带有get捕捉器的默认值
最常见的捕捉器是用于读取、写入的属性,要拦截读取操作,handler应该有get(target,property,receiver)方法,读取属性时触发该方法。
target – 目标对象,该对象被作为第一个参数传递给new Proxy
property – 目标属性名
receiver – 如果目标属性时一个getter访问器属性,则receiver就是本次读取属性所在的this对象。通常这就是proxy对象本身。
let dictionary = {1:“halo”,2:“ray”}
dictionary = new Proxy(dictionary ,{
get(target,phrase){
if(phrase in target){return target[phrase]}
else{ return phrase}
} //使用get进行拦截

使用set捕捉器进行验证
当我们想要一个专门用于数字的数组,如果添加了其他类型的值,则应该抛出一个错误,当写入属性时set捕捉器被触发,set(target,property,value,receiver)
target – 目标对象,该对象被作为第一个参数传递给new Proxy
property – 目标属性名称
value – 目标属性的值
receiver – 与get捕捉器相似,仅与setter访问器属性相关
let numbers = []
numbers = new Proxy(numbers , {
set(target,prop,val){
if( typeof val == ‘number’){ target[prop] = val return true}
else{ return false}
}
}) //使用setting写入,set验证新值
numbers.push(1) //添加成功

使用“ownKeys”和“getOwnPropertyDescriptor”进行迭代
Object.keys,for…in循环和大多数其他遍历对象属性的方法都使用内部方法[OwnPropertyKeys]来获取属性列表。

Object.getOwnPropertyNames(obj) – 返回非Symbol键
Object.getOwnPropertySymbols(obj) – 返回Symbol键
Object.keys/values() – 返回带有enumerable标志的Symbol键/值
for…in – 循环遍历所有带有enumerable标志的非Symbol键,以及原型对象的键

具有“deleteProperty”和其他捕捉器的受保护属性
以下划线_开头的属性和方法是内部的,不应该从对象外部访问它们。为防止任何对_开头的属性进行访问,我们使用代理来处理。
get – 读取此类属性时抛出错误
set – 写入属性时抛出错误
deleteProperty – 删除属性时抛出错误
ownKeys – 在使用for…in和像Object.keys这样的方法时排除以_开头的属性。
let user ={ name:“John”,password:"***" }
user = new Proxy( user , {
get(target , prop){ //读取属性信息
if(prop.startsWith(’
’)){ throw new Error (“Access denied”)}
let value = target[prop];
return (typeof value === ‘function’) ? value.bind(target) : value;
}
set(target , prop , val){ //拦截属性写入
if(prop.startsWith( ‘’ )){ throw new Error(“Access denied”)}
else{
target[prop] = val
return true;
}}
deleteProperty(target , prop){。//拦截属性删除
if(prop.startsWith(’
’)){ throw new Error(“Access denied”)}
else{
delete target[prop];
return true;
}}
ownKeys(target){ //拦截读取属性列表
return Object.keys(target).filter(key=>!key.startsWith(’_’))
}
})

包装函数:apply
apply(target , thisArg , args)捕捉器能使代理以函数的方式被调用,经代理proxy包装再函数周围
target – 目标对象
thisArg – this的值
args – 参数列表

Reflect:是一个内建对象,可简化Proxy的创建,前面所讲的内部方法,例如[[Get]],[[Set]]等,都只是规范性的,不能直接调用。Reflect对象使调用这些内部方法成为了可能,它的方法是内部方法的最小包装。对于每个可悲Proxy捕获的内部方法,在Reflect中都有一个对应的方法,其名称和参数与Proxy捕捉器相同。所以我们可以使用Reflect来将操作转发给原始对象。

Proxy的局限性
1、内建对象具有“内部插槽”,对这些对象的访问无法被代理。
2、私有类字段也是如此,因为它们也是在内部使用插槽实现的,因此,代理方法的调用必须具有目标对象为this才能访问它们。
3、对象的严格相等性 === 无法被拦截
4、性能:基准测试取决于引擎,但通常使用最简单的代理访问属性所需的时间也要长几倍。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值