愚人节快乐。
复习一下 ES6 的 Proxy 新特性,即将到来的 vue3 也将用 Proxy 代替 Object.defineproperty 来实现数据响应式。

Proxy 是什么

Proxy是 ES6 新增的功能,他可以用来自定义对象中的操作(如属性查找,赋值,枚举,函数调用等)。

语法

let p = new Proxy(target, handler);

  • target:用Proxy包装的目标对象,可以是任何类型的对象,包括原生数组,函数,甚至是另一个Proxy.
  • handler: 一个对象,其属性是当执行一个操作时,定义代理行为的函数。

handler

handler是一个占位符对象(placeholder object),包括了Proxy可以自定义的一些方法。

一共有 13 种可代理操作,每种操作的属性名和触发这种操作的方式列举如下。注意,如果没有定义某种操作,那么这种操作会被转发到目标对象身上。

  1. handler.getPrototypeOf()
    在读取代理对象的原型时触发该操作,比如在执行 Object.getPrototypeOf(proxy) 时。
  2. handler.setPrototypeOf()
    在设置代理对象的原型时触发该操作,比如在执行 Object.setPrototypeOf(proxy, null) 时。
  3. handler.isExtensible()
    在判断一个代理对象是否是可扩展时触发该操作,比如在执行 Object.isExtensible(proxy) 时。
  4. handler.preventExtensions()
    在让一个代理对象不可扩展时触发该操作,比如在执行 Object.preventExtensions(proxy) 时。
  5. handler.getOwnPropertyDescriptor()
    在获取代理对象某个属性的属性描述时触发该操作,比如在执行 Object.getOwnPropertyDescriptor(proxy, “foo”) 时。
  6. handler.defineProperty()
    在定义代理对象某个属性时的属性描述时触发该操作,比如在执行 Object.defineProperty(proxy, “foo”, {}) 时。
  7. handler.has()
    在判断代理对象是否拥有某个属性时触发该操作,比如在执行 “foo” in proxy 时。
  8. handler.get()
    在读取代理对象的某个属性时触发该操作,比如在执行 proxy.foo 时。
  9. handler.set()
    在给代理对象的某个属性赋值时触发该操作,比如在执行 proxy.foo = 1 时。
  10. handler.deleteProperty()
    在删除代理对象的某个属性时触发该操作,比如在执行 delete proxy.foo 时。
  11. handler.ownKeys()
    在获取代理对象的所有属性键时触发该操作,比如在执行 Object.getOwnPropertyNames(proxy) 时。
  12. handler.apply()
    当目标对象为函数,且被调用时触发。
  13. handler.construct()
    在给一个目标对象为构造函数的代理对象构造实例时触发该操作,比如在执行new proxy() 时。

这些方法的参数基本上都有多个,各有异同,看文档吧。

Examples

  1. 基础示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    let target = {a:1}
    let handler = {
    get:function(target,property,receiver){
    console.log('target',target)
    console.log('property',property)
    console.log('receiver',receiver)
    return target[property] + '_new'
    },
    set:function(target, property, value, receiver){
    target[property] = value
    return true
    }
    }
    let p = new Proxy(target,handler)
    console.log(p.a) // 1_new
  2. 为每一层都添加监控

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    let target = {
    a:1,
    b:{
    c:2
    }
    }
    let handler = {
    get:function(target,property,receiver){
    console.log('target:',target)
    console.log('property:',property)
    console.log('receiver:',receiver)

    if(typeof target[property] === 'object' && target[property] !== null){
    return new Proxy(target[property], handler);
    }else{
    return Reflect.get(target, property)+'new'
    }
    },
    set:function(target, property, value, receiver){
    target[property] = value
    console.log(target[property],'changed')
    return true
    }
    }
    let p = new Proxy(target,handler)
    p.b.c = 3