Proxy-《ES6 入门教程》阮一峰学习笔记

334 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第8天,点击查看活动详情

前言

我们都知道vue2中的响应式是通过Object.definePropertygetset函数实现的。但是在vue3中对于对象的响应式(reactive)却是通过ES6的新增对象proxy,它与Object.defineProperty究竟有什么区别?他的作用又有什么?

什么是Proxy

Proxy对象可以理解为在目标对象之前架设一层“拦截器”。通过英文意思[代理]也可以得出,它是目标对象的代理对象,来处理对于目标对象的某些操作。就是说,对于目标对象的操作需要先通过代理对象。所以,我们可以通过Proxy对象来对目标对象的访问进行过滤与改写。

Proxy的语法

var proxy = new Proxy(target, handler);

proxy表示生成的Proxy实例对象

target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截操作行为。

下面是 Proxy 支持的拦截操作一览,一共 13 种。

  • get(target, propKey, receiver) :拦截对象属性的读取,比如proxy.fooproxy['foo']
  • set(target, propKey, value, receiver) :拦截对象属性的设置,比如proxy.foo = vproxy['foo'] = v,返回一个布尔值。
  • has(target, propKey) :拦截propKey in proxy的操作,返回一个布尔值。
  • deleteProperty(target, propKey) :拦截delete proxy[propKey]的操作,返回一个布尔值。
  • ownKeys(target) :拦截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
  • getOwnPropertyDescriptor(target, propKey) :拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
  • defineProperty(target, propKey, propDesc) :拦截Object.defineProperty(proxy, propKey, propDesc)Object.defineProperties(proxy, propDescs),返回一个布尔值。
  • preventExtensions(target) :拦截Object.preventExtensions(proxy),返回一个布尔值。
  • getPrototypeOf(target) :拦截Object.getPrototypeOf(proxy),返回一个对象。
  • isExtensible(target) :拦截Object.isExtensible(proxy),返回一个布尔值。
  • setPrototypeOf(target, proto) :拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
  • apply(target, object, args) :拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)proxy.call(object, ...args)proxy.apply(...)
  • construct(target, args) :拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)

可以看到,相比于Object.definePropertygetset的拦截操作,proxy提供了更多拦截操作去实现更复杂的逻辑应用。

Proxy的应用

get与set

get方法在读取对象的属性时触发。get方法有三个参数,target:目标对象,propKey:读取的属性值,receiver:实例化Proxy对象本身。receiver为可选参数。

    const a = {
    name: 'Alan',
    age: 24,
    hobby: 'play'
    }
    const p = new Proxy(a, {
    get(target, propKey, receiver) {
      console.log(target, propKey, receiver)
      return target[propKey]
    }
    })
    p.age  //{name: 'Alan', age: 24, hobby: 'play'}age: 24hobby: "play"name: "Alan"[[Prototype]]: Object 'age' Proxy {name: 'Alan', age: 24, hobby: 'play'}

set方法在赋值对象某个属性时触发。set方法相比于get多了value一个所赋的值。

      const a = {
        name: 'Alan',
        age: 24,
        hobby: 'play'
      }
      const p = new Proxy(a, {
        set(target, propKey, value, receiver) {
          console.log(target, propKey, value, receiver)
          target[propKey] = value
          return true
        }
      })
      p.age = 26//{name: 'Alan', age: 24, hobby: 'play'}age: 24hobby: "play"name: "Alan"[[Prototype]]: Object 'age' Proxy {name: 'Alan', age: 24, hobby: 'play'}
      console.log(a.age)  //26

deleteProperty

deleteProperty方法在delete对象某个属性时触发。传入两个参数,一个是目标对象,一个是属性名。

      const p = new Proxy(a, {
        deleteProperty(target, propKey) {
          console.log(target, propKey)
          delete target[propKey]
          return true
        }
      })
      delete p.name//{name: 'Alan', age: 24, hobby: 'play'} 'name'
      console.log(a.name) //undefined

对于拦截方法的使用就说明到这里,大家感兴趣的话可以自己试一试其他的方法。

总结

对于Es6 Proxy的讲解就是这么多了,学完之后相信大家对于vue3的响应式处理也会有一定的了解,希望大家能有所收获!