ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

浅析 ES6 Proxy

2022-03-26 02:00:38  阅读:180  来源: 互联网

标签:ES6 set target person proxy receiver 浅析 Proxy


1 关于Proxy

Proxy,代理,是ES6推出的一个特性。通过Proxy我们可以拦截对象的某些操作,并在其中加入定制化代码,使这些操作变得更加丰富灵活

语法:

let proxy = new Proxy(target,handle)

Proxy表示创建代理实例的类,target为被代理的对象,handle为拦截器对象,内含拦截器方法

2 Proxy常用API

  • set 拦截属性写入
  • get 拦截属性读取
  • deleteProerty 拦截属性删除
  • has 拦截in操作符
  • revocable 撤销proxy
    ...

2.1 Proxy api示例: set

Proxy有许多方法,这里挑一个最具代表性的set方法作为示例

在Proxy Handle中,set用于拦截对target对象的属性赋值操作
set拥有四个参数,分别是被代理对象target,属性名key,属性值value以及set方法的调用者receiver(类比于this一般指向调用者obj),这个receiver一般是proxy,但也有例外,后续会说明

在代理之后,一旦通过proxy为对象属性赋值,proxy中的set(如果有)就会执行。反之则不会,即直接为对象赋值并不会触发Proxy set方法

let person = {name:'island'}
let proxy = new Proxy(person,{
    set(target,key,value,receiver){
        console.log('set run')
        return Reflect.set(target,key,value,receiver)
    }
})
proxy.name = 'proxy set'  // set run
person.name = 'person set' // nothing happen

target一定是被代理对象。而receiver一般是代理对象proxy,但其实是指向set方法的调用者

let person = {name:'island'}
let proxy = new Proxy(person,{
    set(target,key,value,receiver){
        console.log(target === person) // true
        console.log(receiver === proxy ) // true 
    }
})
proxy.name = 'person set'

如果将proxy写入到person的原型上,此时给person设置属性时,js会从person自身向原型链上找到set并执行,此时set的调用者是person而非peoxy,相应的receiver也变成了person,因此我们说receiver是set方法的调用者而并非一定是proxy

let person = {};
let proxy = new Proxy({}, {
    set(target, prop, value, receiver) {
        console.log(receiver === person) // true
        console.log(receiver === proxy) // false
    }
});
Object.setPrototypeOf(person, proxy);
person.name = 'island'

注意:

  • set方法只能对target的可写属性(writable为true)作用,writable为false时set失效
  • set方法中不建议对receiver再进行属性赋值,因为这会重复触发set执行,抛出函数执行栈溢出异常。同理,我们也不建议在get方法中对receiver做属性读取操作
let person = {name:'isand'};
let proxy = new Proxy(person, {
    set: function (target, prop, value, receiver) {
        receiver[prop] = value //  Maximum call stack size exceeded

    },
    get(target,key,receiver){
        receiver[key] //  Maximum call stack size exceeded
    }
});
proxy.name = 'yoke'

3 Proxy的应用

Proxy以其自身丰富灵活的特点,可以在许多开发场景中得到应用
在工程中,前端是UI与后台数据的桥梁,我们经常遇见的一个场景是将接口数据中的时间戳进行格式化展现给用户,或者对表单的输入进行合理的校验,而这些都可以通过Proxy代理来完成

4 Proxy与被代理对象的关系

无论是通过Proxy改变被代理对象target的属性,还是直接改变被操作对象target的属性,在Proxy对象与target中都能得到反馈。关于Proxy与被代理对象target究竟是什么关系,本人也一直很疑惑。有人倾向于认为是浅拷贝,但我认为不是,如果是浅拷贝,那么改变了非引用类型的属性时,是不会作用到对方内存中的。但事实显然与之相悖。无论是改变proxy.string还是target.string,都能在对方身上看到效果,所以二者并不是浅拷贝的关系。
我暂且觉得Proxy与target之间是有能力访问同一块内存的关系,但又并非是赋值引用,因为proxy === target 值为假。这个问题暂且放着,可能要等到日后阅读V8实现,或者社区有人阅读了V8相关实现并对外分享之后才能得到解释了

5 Proxy与Object.defineProperty的差异

Object.defineProperty中也有get,set方法,这与Proxy有一部分共性。Vue2.0中使用了Object.defineProperty来进行数据劫持,实现数据双向绑定。但Vue3.0中尤大又用Proxy重写了这一特性,从而不得不引发大家关于Proxy与Object.defineProperty之间差异性的讨论

从语义上来说,defineProperty专注于属性被操作时的权限,比如属性是否可修改--writable,是否可配置--configurable。而Proxy更在意属性被操作时的行为,比如对象属性被遍历之前时应当做些什么--has,被删除之前应该做些什么--deleteProerty

从细节上来说,Proxy对属性监听的更深入一些。定义一个属性值为数组,对于Proxy,数组内部元素的改变也会被侦听到,触发set trap。而defineProperty的set只在属性指向地址更改的时候才执行,而数组内部元素的改变并不会被侦听到,正是由于defineProperty的这一特征,导致Vue2.0需要重写数组原型上的push、splice等方法

let arrProxy = new Proxy([], {
    set(target, key, value, receiver) {
        console.log('run Proxy set')
    }
})

let target = {}
Object.defineProperty(target,'arr',{
    set:function(){
        console.log('run defineProperty set ')
    }
})

arrProxy.push(1) // run Proxy set
target.arr.push(1) // nth happpen

标签:ES6,set,target,person,proxy,receiver,浅析,Proxy
来源: https://www.cnblogs.com/ltfxy/p/16057495.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有