写点什么

ES6 中的 Proxy

  • 2023-04-15
    广东
  • 本文字数:2369 字

    阅读完需:约 8 分钟

ES6中的Proxy

前言

如果我们想要监视对象中的属性读写,可以使用 ES5 提供的Object.defineProperty()为对象添加属性,就可以捕获到对象中的属性读写的过程,这种方法应用非常广泛,在 vue3.0 之前的版本就是使用Object.defineProperty()实现数据响应来完成数据的双向绑定


在 ES6 中全新设计了一个叫Proxy的类型,Proxy 这个词的原意是代理,用在这里表示由它来"代理"某些操作,可以译为"代理器",Proxy就是专门为对象设置访问代理器的,无论是读还是写都要经过代理,通过Proxy就能轻松监视对象的读写过程

Proxy 的使用

如何使用Proxy监视对象的读写过程呢?定义一个 person 对象,对象当中有一个 name 属性和 height 属性,然后通过new Proxy的方式为 person 创建一个代理对象


Proxy 的构造函数需要 2 个参数,一个是需要代理的目标对象,另一个是代理的处理对象,在这个处理对象中可以通过get()方法监视对象属性的访问,通过set()方法监视对象设置属性的过程


const person={    name:'zzz',    height:185}const proxy=new Proxy(person,{    get(){//监视对象属性的访问
}, set(){//监视对象设置属性的过程
}})
复制代码

get()方法

get()方法可以接收两个参数,第一个是代理的目标对象,第二个是访问的属性名,分别把它们打印出来


const proxy=new Proxy(person,{    get(target,property){// 目标对象  访问的属性名        console.log(target,property);    },    set(){
}})console.log(proxy.name);
复制代码



get()方法正常的逻辑应该是判断代理目标对象中是否存在访问的属性名,存在就返回对应的值,不存在就返回 undefined 或者一个默认值


get(target,property){    return property in target? target[property]:'default'},
//分别打印存在的属性和不存在的属性console.log(proxy.name); //zzzconsole.log(proxy.age); //default
复制代码

set()方法

set()方法接收三个参数,第一个是代理的目标对象,第二个是要写入的属性名,第三个是要写入的属性值,分别在方法里将这三个参数打印出来并在外边添加一个属性


set(target,property,value){    console.log(target,property,value);}
proxy.sex='男'
复制代码


控制台就会打印出写入的属性和属性值



set()方法正常的逻辑应该是为代理目标设置指定属性,在设置之前先做一些数据校验,例如属性名为 height,那么那么就要判断它的是否是一个数字,不是就抛出错误


set(target,property,value){    if(property === 'height'){ //判断属性名是否为height        if(!Number.isInteger(value)){//判断是否为整数            throw new TypeError(`${value} is not an int`)        }    }    target[property]=value}
复制代码


我们分别在控制台去修改 height 为字符串和整数看看,修改为字符串会抛出错误,修改整数才会被成功修改



Proxy 对比 Object.defineProperty

相比于 Object.defineProperty,Proxy 更为强大

Proxy 能够监视到更多的对象操作

defineProperty 只能监视属性的读写,而 Proxy 可以监视到更多的对象操作,例如 delete 操作,对对象当中方法的调用......


在 Proxy 对象的处理对象当中添加一个deleteProperty()代理方法,这个方法会在外部在对代理对象进行操作的时候自动执行


这个方法接收两个参数,代理目标对象和所要删除的属性名称,在这方法内部打印一下要删除的属性名称,然后正常执行 delete 操作,之后在外部去进行 delete 操作


deleteProperty(target,property){    console.log('delete',property);    delete target[property]},
//外部执行删除操作delete proxy.nameconsole.log(proxy);
复制代码


通过控制台可以看成功拦截了 delete 操作并且执行了删除操作



这是defineProperty做不到的,除了 delete 以外还有很多其他的对象操作能被监视到:


  • apply(tagert,obj,args):拦截函数的调用、callapply操作

  • has(tagert,key):判断对象是否具有某个属性

  • construct(tagert,args):拦截new命令

  • deleteProperty(tagert,key):拦截delete操作

  • defineProperty(tagert,key,desc):拦截Object.defineProperty()操作

  • getOwnPropertyDescriptor(tagert,key):拦截Object.getOwnPropertyDescriptor()

  • getPrototypeOf(tagert):拦截获取对象原型

  • isExtensible(tagert):拦截Object.isExtensible()操作

  • ownKeys(tagert):拦截对象自身属性的读取操作

  • preventExtensions(tagert):拦截Object.preventExtensions()

  • setPrototypeOf(tagert,proto):拦截Object.setPrototypeOf()方法

Proxy 能更好的支持数组对象的监视

以往通过Object.defineProperty监视数组的操作最常见的就是通过重写数组的操作方法了,在 vue.js 里也是使用这种方式通过自定义方法覆盖掉数组原型对象上的 push(),shift()等方法,以此来劫持对应方法调用的过程


那我们如何使用 Proxy 对象来监视数组?先定义一个数组,为这个数组定义一个 Proxy 对象,在这个 Proxy 对象的处理对象上添加set()方法监视数据的写入,将写入的值赋值给目标对象对应的属性名,然后返回一个 true 代表写入成功


const list=[]const proxy=new Proxy(list,{    set(target,property,value){        console.log(target,property,value);        target[property]=value        return true //代表写入成功    }})
复制代码


现在在外部对数组的写入操作都会被监视到,通过这个 proxy 对象的 push 方法往数组中添加数据,就可以看到数据被写入了并且控制台成功打印了,这就是 proxy 对数组的监视,要是放在 Object.defineProperty 上去实现就很麻烦



Proxy 是以非侵入的方式监视对象的读写

一个定义好的对象,Proxy 不需要对对象本身做任何操作就可以监视到对象内部成员的读写,而 Object.defineProperty 要求必须通过特定的方式单独去定义对象中需要被监视的属性,这需要去做很多额外的操作,可以看看上一篇的https://xie.infoq.cn/article/858f2cb49013b2f82d9d4edef使用 Object.defineProperty 对对象属性的监视操作

发布于: 刚刚阅读数: 5
用户头像

你若毁我天堂,我必戳你脊梁 2022-11-01 加入

还未添加个人简介

评论

发布
暂无评论
ES6中的Proxy_JavaScript_格斗家不爱在外太空沉思_InfoQ写作社区