Proxy Reflect | 青训营笔记

57 阅读2分钟

Proxy

在ES6中,新增了一个Proxy类,如果我们希望监听一个对象的相关操作,可以创建一个代理对象,之后对这个对象的所有操作都是通过代理对象来完成,代理对象可以监听想要对原对象的的操作

const per = {
  name: "Allen",
  age: 18
}

const perProxy = new Proxy(per, {
  get(target, key) {
    console.log(`the ${key} has been read`, target)
    return target[key]
  },
  set(target, key, newValue) {
    console.log(`the ${key} has been set to ${newValue}`, target)
    target[key] = newValue
  }
})

perProxy.name = "Tony"
perProxy.age = 15
console.log(per.name)
console.log(per.age)

image.png

set和get分别对应的是函数类型 set有四个参数:

  • target:目标对象
  • property:将被设置的属性key
  • newValue:新属性值
  • receiver:调用的代理对象

get有三个参数:

  • target:目标对象
  • property:被获取的属性key
  • receiver:调用的代理对象

当然proxy还有其他的捕获器

const per = {
  name: "Allen",
  age: 18
}

const perProxy = new Proxy(per, {
  get(target, key) {
    console.log(`the ${key} has been read`, target)
    return target[key]
  },
  set(target, key, newValue) {
    console.log(`the ${key} has been set to ${newValue}`, target)
    target[key] = newValue
  },

  // 监听in操作的捕获器
  has(target, key) {
    console.log(`the ${key} has been detected by in`, target)
    return key in target
  },

  // 监听delete操作的捕获器
  deleteProperty(target, key) {
    console.log(`the ${key} has been deleted`, target)
    delete target[key]
  }
})

console.log("name" in perProxy)
delete perProxy.name

image.png

还有:

  • handler.getPrototypeOf() —— Object.getPrototypeOf的捕获器
  • handler.setPrototypeOf() —— Object.setPrototypeOf的捕获器
  • handler.isExtensible() —— Object.isExtensible的捕获器
  • handler.preventExtensions() —— Object.preventExtensions的捕获器
  • handler.getOwnPropertyDescriptor() —— Object.getOwnPropertyDescriptor的捕获器
  • handler.defineProperty() —— Object.defineProperty的捕获器
  • handler.apply() —— 函数调用操作的捕获器
  • handler.construct() —— new操作符的捕获器

最后两个是针对函数对象的

function foo() {}

const fooProxy = new Proxy(foo, {
  apply(target, thisArg, argArray) {
    console.log("foo is called by apply")
    return target.apply(thisArg, argArray)
  },
  construct(target, argArray, newTarget) {
    console.log("foo is called by new")
    return new target(...argArray)
  }
})

fooProxy.apply({}, [123, 456])
new fooProxy(123, 456)

image.png

Reflect

Reflect是ES6新增的一个API,是一个对象 像第一段Proxy代码,我们创建proxy就是想要不对原对象进行直接的操作,但是proxy里还是直接操作了,这时可以使用reflect配合proxy一起使用

const per = {
  name: "Allen",
  age: 18
}

const perProxy = new Proxy(per, {
  get(target, key) {
    console.log("get-----")
    return Reflect.get(target, key)
  },
  set(target, key, newValue) {
    console.log("set-----")
    const result = Reflect.set(target, key, newValue)
    console.log(result)
  }
})

perProxy.name = "Tony"
console.log(perProxy.name)

这样就可以避免直接我们手动操作这个对象了,而是从语言层面去操作这个对象,而且这个Reflect的set方法还会返回一个布尔值,这样还能清晰的看到设置成功与否

Proxy的receiver参数

在Proxy的get/set中有第三个参数receiver,它实际上是我们创建的代理对象

const per = {
  _name: "Allen",
  get name() {
    return this._name
  },
  set name(newValue) {
    this._name = newValue
  }
}

const perProxy = new Proxy(per, {
  get(target, key, receiver) {
    // receiver是创建出来的代理对象
    console.log(`${key} has been read`)
    return Reflect.get(target, key, receiver)
    // 传过去receiver在原对象中get/set访问自身属性时的this会指向receiver,就是获取_name的过程也可以被监听到
  },
  set(target, key, newValue, receiver) {
    console.log(`${key} is set to ${newValue}`)
    Reflect.set(target, key, newValue, receiver)
  }
})

console.log(perProxy.name)
perProxy.name = "Tony"

image.png

主要还是让代理在原对象中调用get/set的时候也能被监听到