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...inReflect.ownKeys()
apply(target, thisArg, args)实例作为函数调用Reflect.apply()
construct(target, args)使用new实例化ProxyReflect.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实现观察者模式

  1. 首先定义了一个observe 用来存储 要触发的函数
  2. 然后定义了一个observable 函数 对 对象的设值做了一层代理,拦截赋值操作, Reflect.set()用来完成默认的设值行为, 然后触发函数
  3. 每当对象调用对象内部的的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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值