JS学习笔记(八)代理与反射
文章目录
代理和反射为开发者提供了拦截并向基本操作嵌入额外行为的能力。即给目标对象定义一个关联的代理对象,而这个代理对象可以作为抽象的目标对象来使用
一、代理
1.1 创建空代理
默认情况下,在代理对象上执行的所有操作都会无障碍的传播到目标对象。因此,在任何可以使用目标对象的地方,都可通过同样的方式来使用与之相关联的代理对象。
代理是使用:Proxy 构造函数创建。 接收两个参数:目标对象和处理程序对象。两者缺一不可
const target = {
id:'target'
};
const handler = {};
const proxy = new proxy(target,handler);
// id会访问同一个值
console.log(target.id);
console.log(proxy.id);
// 给目标属性赋值会反映到两个对象上,因为两个对象访问的是同一个值
target.id='foo';
console.log(target.id);
console.log(proxy.id);
// 给代理属性赋值会反映到两个对象上,因为这个赋值会转移到目标对象上
proxy.id='bar';
console.log(target.id);
console.log(proxy.id);
//hasOwnProPerty()在两个地方都会引用到目标对象
console.log(target.hasOwnProperty('id'));
console.log(proxy.hasOwnProperty('id'));
//Proxy.prototype是undefined 因此不能用instanceof操作符,会报错
// 严格相等可用来区分代理和目标
console.log(target === proxy); //false
1.2 定义捕获器
是在处理程序对象中定义的“基本操作的拦截器”。每个处理程序对象可以包含零个或者多个**捕获器,每个捕获器都对应一种基本操作,可以直接或间接在代理对象上调用。每次在代理对象上调用这些基本操作时,代理可以在这些操作传播到目标对象之前先调用捕获器,从而拦截并修改相应的行为。
const target = {
foo:'bar'
};
const handler = {
// 捕获器在处理程序对象中以方法名为键
get() {
return 'handle override';
}
};
const proxy = new proxy(target,handler);
console.log(target.foo); // bar
console.log(proxy.foo); // handle override
console.log(target['foo']);// bar
console.log(proxy['foo']);// handle override
console.log(Object.create(target)['foo']);// bar
console.log(Object.create(proxy)['foo']);// handle override
1.3 捕获器参数和反射API
所有捕获器都可以访问相应的参数,基于这些参数可以重建被捕获方法的原始行为。如:get()捕获器会接收目标对象、要查询的属性和代理对象三个参数
处理程序对象中所有可以捕获的方法都有对应的反射API方法。这些方法与捕获器拦截的方法具有相同的名称和函数签名,且具有与被拦截方法相同的行为。
const target = {
foo:'bar',
baz:'qux'
};
const handler = {
get(trapTarget,property,receiver) {
let decoration = '';
if(property === 'foo') {
decoration = '!!!';
}
return Reflect.get(...arguments) + decoration;
}
};
const proxy = new proxy(target,handler);
console.log(target.foo);//bar
console.log(proxy.foo); //bar!!!
console.log(target.baz);//qux
console.log(proxy.baz); //qux
1.4 可撤销代理
对于使用new proxy()创建的普通代理来说,这种联系会在代理对象的生命周期内一直持续存在。proxy暴露revocable()方法,此方法支持撤销代理对象与目标对象的关联
const target = {
foo:'bar'
};
const handler = {
get() {
return 'intercepted';
}
};
const {proxy,revoke} = Proxy.revocable(target,handler);
console.log(target.foo); //bar
console.log(proxy.foo); //'intercepted'
revoke();
console.log(proxy.foo); //TypeError
1.5 实用反射API
1.5.1 反射API与对象API
使用反射API需记住:
- 反射API并不限于捕获处理程序
- 大多数反射API方法在Object类型上有对应的方法
1.5.2 状态标记
很多反射方法返回称作“状态标记”的布尔值,表示意图执行的操作是否成功。
提供状态标记的反射方法:
- Reflect.defineProperty()
- Reflect.preventExtensions()
- Reflect.setPrototypeOf()
- Reflect.set()
- Reflect.deleteProperty()
1.5.3 用一等函数替代操作符
- Reflect.get():替代对象属性访问操作符
- Reflect.set():替代=赋值操作符
- Reflect.has():替代in操作符或者with()
- Reflect.deleteProperty():替代delete操作符
- Reflect.construct():替代new操作符
二、代理捕获器与反射方法
2.1 get()
get()捕获器会在获取属性值的操作中被调用。对应的反射API方法为Reflect.get()
1. 返回值
无限制
2. 拦截的操作
- proxy.property
- proxy[property]
- Object.create(proxy)[property]
- Reflect.get(proxy,property,receiver)
3. 捕获器不变式
若target.property不可写且不可配置,则处理程序返回的值必须与target.property匹配
若target.property不可配置且[[Get]]特性为undefined,处理程序的返回值也必须是undefined。
2.2 set()
set()捕获器在设置属性值的操作值中被调用。
1. 返回值
返回true表示成功,false表示失败。严格模式下会报错
2. 拦截的操作
- proxy.property = value
- proxy[property] = value
- Object.create(proxy)[property] = value
- Reflect.get(proxy,property,value,receiver)
2.3 has()
has()捕获器在设in操作符中被调用。
1. 返回值
必须返回布尔值,表示属性是否存在。返回非布尔值会被转型为布尔值
2. 拦截的操作
- property in proxy
- property in Object.create(proxy)
- with(proxy) { (property); }
- Reflect.has(proxy,property)
2.4 defineProperty()
defineProperty()捕获器在Object.defineProperty()中被调用。
1. 返回值
必须返回布尔值,表示属性定义是否存在。返回非布尔值会被转型为布尔值
2. 拦截的操作
-
Object.defineProperty(proxy, property, descriptor)
-
Reflect.defineProperty(proxy, property, descriptor)
3. 参数
descriptor:包括可选的enumerable、configurable、writable、value、get 和 set定义的对象
2.5 getOwnPropertyDescriptor()
getOwnPropertyDescriptor()捕获器在getOwnPropertyDescriptor()中被调用。
1. 返回值
必须返回对象,或者在属性不存在时返回undefined
2. 拦截的操作
-
Object.getOwnPropertyDescriptor()(proxy, property)
-
Reflect.getOwnPropertyDescriptor()(proxy, property)
2.6 deleteProperty()
deleteProperty()捕获器在delete操作符中被调用。
1. 返回值
必须返回布尔值,表示属性是否删除成功
2. 拦截的操作
-
delete proxy.property
-
delete proxy[property]
-
Reflect.deleteProperty(proxy, property)
2.7 ownKeys()
ownKeys()捕获器在Object.keys及类似方法中被调用。
1. 返回值
必须返回包含字符串或符号的可枚举对象
2. 拦截的操作
-
Object.getOwnPropertyNames(proxy)
-
Object.getOwnPropertySymbols(proxy)
-
Object.keys(proxy)
-
Reflect.ownKeys(proxy)
2.8 getPrototypeOf()
getPrototypeOf()捕获器在Object.getPrototypeOf()中被调用。
1. 返回值
必须返回对象或null
2. 拦截的操作
-
Object.getPrototypeOf(proxy)
-
Reflect.getPrototypeOf(proxy)
-
proxy.__proto__
-
Object.prototype.isPrototypeOf(proxy)
-
proxy instanceof Object
2.9 setPrototypeOf()
setPrototypeOf()捕获器在Object.setPrototypeOf()中被调用。
1. 返回值
必须返回布尔值,表示原型赋值是否成功
2. 拦截的操作
-
Object.setPrototypeOf(proxy)
-
Reflect.setPrototypeOf(proxy)
2.10 isExtensible()
isExtensible()捕获器在Object.isExtensible()中被调用。
1. 返回值
必须返回布尔值,表示target是否可扩展
2. 拦截的操作
-
Object. isExtensible(proxy)
-
Reflect. isExtensible(proxy)
2.11 preventExtensions()
preventExtensions()捕获器在Object.preventExtensions()中被调用。
1. 返回值
必须返回布尔值,表示target是否已经不可扩展
2. 拦截的操作
-
Object. preventExtensions(proxy)
-
Reflect. preventExtensions(proxy)
2.12 apply()
apply()捕获器在调用函数时被调用。
1. 返回值
无限制
2. 拦截的操作
-
proxy(…argumentList)
-
Function.prototype.apply(thisArg,arguementsList)
-
Function.prototype.call(thisArg,…argumentsList)
-
Reflect. apply(target,thisArgument,argumentsList)
2.13 construct()
construct()捕获器在new操作符中被调用。
1. 返回值
construct()必须返回一个对象
2. 拦截的操作
-
new proxy(…argumentsList)
-
Reflect. construct(target, argumentsList, newTarget)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/150432.html