JS学习笔记 - Proxy & Reflect
JS学习笔记 - Proxy & Reflect
Proxy
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写
定义Proxy
ES6提供了Proxy构造函数,用来生成Proxy实例
var proxy = new Proxy(target,handler)
var target = {}
var proxy = new Proxy(target,{
get(target,props,receiver){
return Reflect.get(target,props,receiver)
}
})
捕获器不变式
使用捕获器几乎可以改变所有基本方法的行为,但也不是没有限制。比如,如果目标对象有一个不可配置且不可写的数据属性,那么在捕获器返回一个与该属性不同的值时,会抛出 TypeError
可撤销代理
有时候可能需要中断代理对象与目标对象之间的联系,此时可以使用 Proxy.revocable()
方法返回一个可取消的Proxy实例,内部包含了生成的Proxy
实例和用于取消该实例的revoke()
方法
var target = { foo:123 }
var { proxy, revoke } = Proxy.revocable(target,{})
proxy.foo //123
revoke()
proxy.foo // TypeError: Revoked
Proxy.revocable()
的一个使用场景是,目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问
this问题
在Proxy代理的情况下,目标对象内部的this会指向Proxy代理
const target = {
thisValEqualsProxy() {
return this === proxy;
}
}
const proxy = new Proxy(target, {});
console.log(target.thisValEqualsProxy()); // false
console.log(proxy.thisValEqualsProxy()); // true
Reflect
所有捕获器都可以基于自己的参数重建原始操作,但并非所有捕获器行为都像 get()那么简单。实际上,开发者并不需要手动重建原始行为,而是可以通过调用全局 Reflect 对象上(封装了原始行为)的同名方法来轻松重建
处理程序对象中所有可以捕获的方法都有对应的反射(Reflect)API 方法。这些方法与捕获器拦截的方法具有相同的名称和函数签名,而且也具有与被拦截方法相同的行为
代理 | 覆写的特性 | 默认特性 |
---|---|---|
get(target, props, receiver) | 读取属性值 | Reflect.get() |
set(target, props, value, receiver) | 写入属性值 | Reflect.set() |
has(target, props) | in操作 | Reflect.has() |
deleteProperty(target, prop) | delete操作符 | Reflect.deleteProperty() |
getPrototypeOf(target) | Object.getPrototypeOf() | Reflect.getPrototypeOf() |
setPropertyOf(target, proto) | Object.setPropertyOf() | Reflect.setPrototypeOf() |
isExtensible(target) | Object.isExtensible() | Reflect.isExtensible() |
preventExtensions(target) | Object.preventExtensions() | Reflect.preventExtensions() |
getOwnPropertyDescriptor(target, prop) | Object.getOwnPropertyDescriptor() | Reflect.getOwnPropertyDescriptor() |
defineProperty(target, prop, desc) | Object.defineProperty() | Reflect.defineProperty() |
ownKeys(target) |
Object.getOwnPropertyNames() Object.getOwnPropertySymbols() Object.keys() for...in
|
Reflect.ownKeys() |
apply(target, thisArg, args) | 实例作为函数调用 | Reflect.apply() |
construct(target, args) | 使用new实例化Proxy | Reflect.construct() |
应用
跟踪属性访问
通过捕获get、set、has等操作,可以知道对象属性什么时候被操作。把实现响应捕获器的某个代理对象放到应用中,可以监控这个对象何时在何处被访问过
const user = {
name: 'Jake'
};
const proxy = new Proxy(user, {
get(target, property, receiver) {
console.log(`Getting ${property}`);
return Reflect.get(...arguments);
},
set(target, property, value, receiver) {
console.log(`Setting ${property}=${value}`);
return Reflect.set(...arguments);
}
});
proxy.name; // Getting name
proxy.age = 27; // Setting age=27
隐藏属性
代理的内部实现对外部代码是不可见的,因此要隐藏目标上的属性也轻而易举
const hiddenProps = {'city','money'}
const userInfo = {
name: 'LiMing',
age: 13,
money: 2000,
city: 'Shenyang',
country: 'China'
}
const proxy = new Proxy(userInfo,{
get(target, props, receiver){
if(hiddenProps.includes(props)){
return undefined
}else{
Reflect.get(target, props, receiver)
}
},
has(target, prop){
if(hiddenProps.includes(prop)){
return false
}else{
Reflect.has(target, prop)
}
}
})
proxy.name // Liming
proxy.city // undefined
proxy.age // 13
'name' in proxy // false
'age' in proxy // true
属性验证
可以根据所赋的值决定是否允许还是拒绝赋值
const target = {
onlyNumbersGoHere: 0
};
const proxy = new Proxy(target, {
set(target, property, value) {
if (typeof value !== 'number') {
return false;
} else {
return Reflect.set(...arguments);
}
}
});
proxy.onlyNumbersGoHere = 1;
console.log(proxy.onlyNumbersGoHere); // 1
proxy.onlyNumbersGoHere = '2';
console.log(proxy.onlyNumbersGoHere); // 1
函数与构造函数参数验证
跟保护和验证对象属性类似,也可对函数和构造函数参数进行审查
例:使函数只接收某种类型的值
function getA(a,b){
console.log(a)
console.log(b)
}
var proxy = new Proxy(getA,{
apply(target,thisArg,args){
for(let item of args){
if(typeof item !== 'number'){
throw new TypeError(`Non-number arg ${item}`)
}
}
return Reflect.apply(...arguments)
}
})
proxy.apply(null,[123,341,'aaa',false,123])
// Uncaught TypeError: Non-number arg aaa
例:要求实例化对象时必须给构造函数传参
class User{
constructor(id) {
this.id_ = id;
}
}
const proxy = new Proxy(User, {
construct(target, argumentsList, newTarget) {
if (argumentsList[0] === undefined) {
throw 'User cannot be instantiated without id';
} else {
return Reflect.construct(...arguments);
}
}
});
new proxy(1);
new proxy();
// Error: User cannot be instantiated without id
Proxy实现观察者模式
- 首先定义了一个observe 用来存储 要触发的函数
- 然后定义了一个observable 函数 对 对象的设值做了一层代理,拦截赋值操作,
Reflect.set()
用来完成默认的设值行为, 然后触发函数 - 每当对象调用对象内部的的set方法时,就会遍历触发我们添加进callbacks里的回调函数
var funcQueue = new Set()
var observer = fn => funcQueue.add(fn)
var observable = object => new Proxy(object,{set})
function set(target,prop,value,receiver){
let result = Reflect.set(...arguments)
funcQueue.forEach(func=>func())
return result
}
function response(){
console.log(data)
}
observer(response)
var data = { name:'Lihua', age:13 }
var person = observable(data)
person.name = 'Liming'
person.age = 20
上一篇: java系统版本比较
下一篇: 应用程序版本号比对