js的Proxy和Reflect


ES6之前,如果我们对对象进行 新增或者删除属性时,仅靠原有的 Object.defineProperty是无法 监听的,基于此,在 ES6中就推出了 ProxyReflect,简单地说, ProxyReflect只做了一件事,就是将 对象原有的基本操作暴露出来或者拦截原有操作来供我们使用

Proxy

如果我们想监听一个对象的所有操作,我们就可以创建一个Proxy对象用于代理原对象,之后对原对象所有的操作都将会由代理对象执行
在实例化一个Proxy的时候需要传入两个参数,一个为需要监听的对象,一个处理对象handler

let obj = {
    name: '张三',
    age: 18
}
let objProxy = new Proxy(obj, {

})

很显然我们需要再handler中写一些东西,那么应该写一些什么呢
我们需要在handler中定义捕获器

对象的基本操作

在了解什么是捕获器之前,我们需要先知道对象的基本操作
事实上,无论是对对象的读取还是赋值,都会触发对象的内部方法 [[GET]][[SET]],这些方法我们平时无法在外部访问
对象一共有11种基本操作,如果对象是函数的话还会额外再多出两种基本操作,所以在JavaScript中对象一共有13种基本操作,Proxy中有13种捕获器,每种捕获器都与一个基本操作相对应,我们可以定义不同的捕获器来拦截对象原本的基本操作

常见捕获器

这些捕获器我们都定义在handler
当用户访问代理对象的属性时,代理对象的基本操作被get捕获器拦截,在拦截器中我们定义针对于原对象的操作,基于此我们才能实现对对象的全面监听
以下是一些常见的捕获器

get

get对应的是基本操作里的[[GET]],定义如下

let obj = {
    name: '张三',
    age: 18
}
let objProxy = new Proxy(obj, {
    get: function (target, key, receiver) {
        console.log(`getting ${key}!`)
        return target[key]
    }
})
console.log(objProxy.name)

结果

get函数有三个参数,target为被监听的对象,key为访问的属性名,receiver是被调用的代理对象

set

set对应基本操作里的[[SET]],它的基本定义如下

let obj = {
    name: '张三',
    age: 18
}
let objProxy = new Proxy(obj, {
    get: function (target, key, receiver) {
        console.log(`getting ${key}!`)
        return target[key]
    },
    set: function (target, key, value, receiver) {
        console.log(`setting ${key}!`)
        target[key] = value
    }
})
console.log(objProxy.name)
objProxy.age = 20
objProxy.sex = "男"

结果
set函数有四个参数,target为原对象,key为设置的属性,value为新的值,receiver为代理对象
可以看到无论是设置已有的属性还是设置未拥有的属性set都能监听到

函数相关拦截器

函数相关的拦截器一共有两个,以下是它的简单定义

let func = function (num) {
    console.log(1 + num)
}
let funcProxy = new Proxy(func, {
    apply: function (target, ctx, args) {
        console.log(target, ctx, args)
        return target(...args)
    }
})
funcProxy(2)
let Func = function (num) {
    this.num = num
}
let FuncProxy = new Proxy(Func, {
    construct: function (target, args) {
        console.log(target, args)
        return new target(...args)
    }
})
let obj = new FuncProxy(2)
console.log(obj.num)

结果

Reflect

如果说Proxy可以拦截对象的基本操作,那么Reflect就可以将对象的基本操作暴露出来直接使用

Reflect与基本语法的区别

我们先看下面两段代码

let obj = {
    name: "张三",
    age: 18
}
Reflect.set(obj, "age", 20)
let obj = {
    name: "张三",
    age: 18
}
obj.age = 20

这两种写法是在结果上相同的,区别在于Reflect是直接调用了[[SET]]方法,而第二种只是间接调用
这么说可能并不能说明Reflect基本语法的区别,让我们来看下一个例子

let obj = {
    name: "张三",
    age: 18,
    get info() {
        return `姓名:${this.name},年龄:${this.age}`
    }
}
console.log(obj.info)

这段代码会执行两个步骤:

  1. 寻找thisinfo是一个函数,它由obj来调用,所以this指向了obj
  2. 调用[[GET]],传入原对象,属性名,以及this,得到返回结果

而如果是Reflect的话则会不同

let obj = {
    name: "张三",
    age: 18,
    get info() {
        return `姓名:${this.name},年龄:${this.age}`
    }
}
Reflect.get(obj, "info", obj)

Reflectget方法所需的参数与[[GET]]方法一致,所以只会执行一个步骤

  1. 因为this已经指定,所以直接调用[[GET]]方法

因为Reflect可以指定this,所以我们可以这么写

let obj = {
    name: "张三",
    age: 18,
    get info() {
        return `姓名:${this.name},年龄:${this.age}`
    }
}
Reflect.get(obj, "info", { name: "李四", age: 30 })

结果

这种写法是无法在基本语法层面里展现的

Reflect常见方法

因为对象有13种基本操作,所以Reflect也有13个方法
以下是一些常用方法

  1. Reflect.get(target, key [, receiver])
    获取target上某个值
  2. Reflect.set(target, key, value [, receiver])
    设置target上某个属性的值
  3. Reflect.has(target, key)
    判断一个target上是否有key属性

Proxy和Reflect

很多时候我们都是将ProxyReflect合在一起来使用

let obj = {
    name: "张三",
    age: 18,
    get info() {
        return `姓名:${this.name},年龄:${this.age}`
    }
}
let objProxy = new Proxy(obj, {
    get(target, key) {
        console.log(`getting ${key}`)
        return target[key]
    }
})
console.log(objProxy.info)

我们有这么一段代码,它的运行结果如下
结果
看起来似乎没什么问题,但仔细想想好像有哪里不对,我们在访问info时同时访问了nameage,但他们并没有在控制台输出getting,但是偏偏infogetting打印出来了,我们对obj设置了get的拦截器,那所有的get操作应该都会被拦截才对,这是为什么呢
因为this的指向错了,我们在控制台打印一下this,现在把代码稍微修改一下

let obj = {
    name: "张三",
    age: 18,
    get info() {
        console.log(this)
        return `姓名:${this.name},年龄:${this.age}`
    }
}

结果
真相出来了,我们在info内的this指向了原始对象,没有经过proxy,自然没有触发拦截器,解决这个问题很简单,使用Reflect即可

let obj = {
    name: "张三",
    age: 18,
    get info() {
        console.log(this)
        return `姓名:${this.name},年龄:${this.age}`
    }
}
let objProxy = new Proxy(obj, {
    get(target, key, recevier) {
        console.log(`getting ${key}`)
        return Reflect.get(target, key, recevier)
    }
})
console.log(objProxy.info)

结果

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值