Proxy-Reflect

Proxy

为了解决上述问题实现监听,es6引入了Proxy。Proxy是一个类,通过其实例化可以帮助我们创建一个代理对象。

1,Proxy监听对象

Proxy的基本使用

如果我们希望监听一个对象的相关操作,我们可以先创建一个代理对象(通过new Proxy来创建obj的代理对象objProxy)

实例化objProxy也就是new Proxy的过程需要接收两个参数,第一个参数为需要创建代理的目标对象(必填);第二个参数为捕获器(非必填项,可传入{}

const obj = {
  name: "why",
  age: 18
}
//对obj对象进行监听
//1 首先通过Proxy方法创建一个proxy对象
//2 new Proxy并传入参数,第一个参数为想监听的obj对象,第二个参数为捕获器(可以为空值),为空值就是不对任何捕获器进行重写
const objProxy = new Proxy(obj, {
})

此时:
1,代理objProxy对象,可以直接访问原对象obj身上的所有属性(如使用objProxy.name可以获取到’why’)
2,对objProxy上的属性进行修改操作后,原对象obj上的属性值也会随之被修改(如修改objProxy.name=‘aaa’,obj.name也会变成’aaa’)
注:这些都是在没有传入任何捕获器的情况下,即捕获器传入的空对象{},没有捕获器对代理对象的修改就直接对原对象产生了影响)

捕获器

完成了初始化对象的代理后,别忘了我们一开始的目的是监听对象中对数据的操作/访问,想要完成这个目标就需要利用捕获器,对Proxy中的捕获器进行重写。

捕获器的使用

我们自己定义的捕获器方法会覆盖掉系统内部本身默认的捕获器,当我们满足某些条件的时候就会自动触发对应的捕获器(如进行获取值操作的时候触发get捕获器)

这里先以get捕获器为例,当访问objProxy.name的时候会自动触发get捕获器。对传入的get捕获器进行方法的重写以满足我们的需要。get捕获器自动接收多个参数,分别是target(目标对象),key(目标对象的属性),receiver(代理对象)

在这里插入图片描述
与之类似的修改值时会触发set捕获器,参数与get类似,多了个newValue(传入的新值)
在这里插入图片描述

其他捕获器

Proxy共13个捕获器,除去上述的get/set捕获器外,还有设置/获取对象原型的捕获器,阻止对象添加新属性的捕获器,查询对象中是否存在某个属性的捕获器等等

使用频率最高的是has捕获器(判断某个属性是否in对象),get捕获器,set捕获器,deleteProperty捕获器(判断是否执行了delete操作符的捕获器)

js中函数的本质也是一个对象,所以new Proxy的目标对象也可以为函数,也存在针对函数的捕获器。apply捕获器(监听函数是否被调用),construct捕获器(监听是否执行new操作)

Reflect

Reflect也是ES6新增的一个api,Reflect本身是一个对象,常常和Proxy一起使用,是用来替代Object的

Reflect的用处

Reflect本身就是一个对象,所以用的时候可以直接用,调用这个对象上的方法即可,(不用像他的好兄弟proxy一样new出来用)
提供了很多操作JS对象的方法,类似Object中操作对象的方法
在这里插入图片描述Reflect提供了很多操作JS对象的方法,有点像Object中操作对象的方法,很多方法本质上和Object身上的方法都是一样的(当前某些方法会同时存在于Reflect和Object上),未来的新方法只会部署在Reflect上

之所以多出来一个Reflect和Object做一样的事情是因为,早期ECMA标准没有考虑到对对象本身的操作如何设计更加规范,所以将很多方法的api都直接放到了Object上面。但是Object作为一个构造函数,这些操作实际上放在Object身上并不合适,所以ES6中多了Reflect,并将方法都放到了Reflect上

Reflect的常见方法

在这里插入图片描述

Reflect和Proxy配合使用

首先明确,我们创建代理对象的目的就是不在原对象上进行直接的修改,所以我们获取/修改都是在代理对象上修改的。但是在上文中的Proxy使用中,对Proxy的操作同时对原对象进行了修改——对objProxy的值进行修改,obj也受到了影响,事与愿违。

const obj = {
  name: "why",
  age: 18
}

const objProxy = new Proxy(obj, {
  get: function(target, key, receiver) {
  	return target[key]
  },
  set: function(target, key, newValue, receiver) {
  }
})

objProxy.name = "kobe"
console.log(objProxy.name)//kobe
console.log(obj.name)//kobe

为了解决这一问题就需要用到Reflect,不再直接对原对象进行操作,而是通过Reflect创建原对象的映射,避免对原对象直接访问和操作。

Reflect上有一些方法与Proxy的捕获器一一对应,如Relect.get对应Proxy上的get捕获器,将上面代码调整为

const obj = {
  name: "why",
  age: 18
}

const objProxy = new Proxy(obj, {
  get: function(target, key, receiver) {
    console.log("get---------")
    return Reflect.get(target, key)
    //通过Reflect的映射避免了对原对象的直接访问/操作
    //对get捕获器进行重写,返回结果不再是原对象,而是通过Reflect上的get方法,Reflect上的方法和Proxy捕获器方法一一对应
    //这里Reflect.get也接收三个参数,和get捕获器接收的参数一致,target原对象,key原对象的属性,receiver
   
  },
  set: function(target, key, newValue, receiver) {
    console.log("set---------")
    target[key] = newValue
    Reflect.set(target, key,newValue)
    //操作和get捕获器类似
    const result = Reflect.set(target, key, newValue)
    if (result) {
    //设置成功执行XXX
    } else {
    //设置失败执行XXX
    }
    //通过Reflect.set设置值会返回一个布尔值表示这次修改值的操作是否成功(如object.freeze的话操作就会失败返回false),通过监听设置的结果我们可以执行一些对应的操作
  }  
})

objProxy.name = "kobe"
console.log(objProxy.name)

receiver参数

只有Proxy对象中get和set捕获器中的receiver参数。简而言之receiver参数指代的是代理对象(也就是上文中的objProxy), objProxy创建的时候会把本身作为receiver参数传入get和set捕获器,虽然和objProxy本质上是一个东西,但是规定不能直接用外面的objProxy,而要用receiver的形式

receiver可以改变原obj对象中getter/setter的this指向

当使用receiver改变了obj中getert/setter方法中的this指向,将其从指向obj改为指向objProxy中,这样才能让objProxy中get/set捕获器里面编写的拦截方法变得有意义了

//这里的get和set是js的obj中的关键字,通过关键字可以创建getter和setter函数。get函数没有参数,set函数会将等号右边的值作为参数,setter和getter连用可以创建一个伪属性,不能在具有真实值的属性上同时拥有一个setter函数
//
const obj = {
  _name: "why",
  get name() {
    return this._name
  },
  set name(newValue) {
    this._name = newValue
  }
}

const objProxy = new Proxy(obj, {
  get: function(target, key, receiver) {
    // receiver是创建出来的代理对象
    console.log("get方法被访问--------", key, receiver)
    console.log(receiver === objProxy)
    return Reflect.get(target, key, receiver)
  },
  set: function(target, key, newValue, receiver) {
    console.log("set方法被访问--------", key)
    Reflect.set(target, key, newValue, receiver)
  }
})

// console.log(objProxy.name)
//reflect的get和set方法同样可以传入receiver作为第三个参数,当receiver作为reflect的get方法的参数时,会改变原obj对象上的this指向,将其从指向自身(obj)改为指向代理对象(这里就是objProxy)
//使用console.log(objProxy.name)通过代理对象访问name属性的时候就会再次触发get方法,也就是这里会访问两次objProxy的get方法,一次是name被访问的时候触发的,
//另一次是_name被访问时触发的,获取objProxy.name时候触发get方法,这个是肯定的,还有另一次我理解是objProxy.name是通过obj对象获取的,也就是obj.name,但是receiver改变了obj中的this指向,指向了objProxy,所以又触发了一次objProxy身上的get

objProxy.name = "kobe"

Reflect中construct的作用

改A构建出来的东西的原型为B

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值