ES6 之 defineProperty 与 proxy


作者:Seiya

时间:2019年08月28日


Object.defineProperty


ES5 提供了 Object.defineProperty 方法,该方法可以在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。参数如下:

  • obj: 要在其上定义属性的对象。

  • prop: 要定义或修改的属性的名称。

  • descriptor: 将被定义或修改的属性的描述符。


简单介绍 descriptor 的几个属性:

  • enumerable:属性是否可枚举,默认 false。

  • configurable:属性是否可以被修改或者删除,默认 false。

  • writable:属性是否可以被赋值运算符改变,默认 false。

  • get:获取属性的方法。

  • set:设置属性的方法。


注意:

属性描述符必须是数据描述符或者存取描述符两种形式之一,不能同时是两者。





Setters 和 Getters

存取描述符中的 get 和 set,又被称为 getter 和 setter。由 getter 和 setter 定义的属性称做”存取器属性“。


  • 当程序查询存取器属性的值时,JavaScript 调用 getter 方法。这个方法的返回值就是属性存取表达式的值。

  • 当程序设置一个存取器属性的值时,JavaScript 调用 setter 方法,将赋值表达式右侧的值当做参数传入 setter。从某种意义上讲,这个方法负责“设置”属性值。 可以忽略 setter 方法的返回值。





proxy

使用 defineProperty 只能重定义属性的读取(get)和设置(set)行为,到了 ES6,提供了 Proxy,可以重定义更多的行为,比如 in、delete、函数调用等更多行为。

Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。


Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。


proxy 的使用

var proxy = new Proxy(target, handler);
  • target 参数表示所要拦截的目标对象;

  • handler 参数也是一个对象,用来定制拦截行为;


我们可以这样使用:

var proxy = new Proxy({}, {
  get: function(obj, prop) {
    console.log('设置 get 操作')
    return obj[prop];
  },
  set: function(obj, prop, value) {
    console.log('设置 set 操作')
    obj[prop] = value;
  }
})

除了 get 和 set 之外,proxy 可以拦截多达 13 种操作:

  • handler.apply() 方法用于拦截函数的调用。

  • handler.construct() 方法用于拦截new 操作符. 为了使 new 操作符在生成的 Proxy 对象上生效,用于初始化代理的目标对象自身必须具有[[Construct]]内部方法。

  • handler.defineProperty() 用于拦截对对象的 Object.defineProperty() 操作。

  • handler.deleteProperty() 方法用于拦截对对象属性的 delete 操作。

  • handler.get() 方法用于拦截对象的读取属性操作。

  • handler.getOwnPropertyDescriptor() 方法是 Object.getOwnPropertyDescriptor() 的钩子。

  • handler.getPrototypeOf() 是一个代理方法,当读取代理对象的原型时,该方法就会被调用。

  • handler.has() 方法是针对 in 操作符的代理方法。

  • handler.isExtensible() 方法用于拦截对对象的Object.isExtensible()。

  • handler.ownKeys() 方法用于拦截 Reflect.ownKeys(),也就是拦截对象自身属性的读取操作。

  • handler.preventExtensions() 方法用于设置对 Object.preventExtensions() 的拦截。

  • handler.set() 方法用于拦截设置属性值的操作。

  • handler.setPrototypeOf() 方法主要用来拦截 Object.setPrototypeOf()


关于以上拦截行为的具体用法,可以查看阮一峰老师的 《ECMAScript 6 入门

最后更新时间: 2019-8-28 11:24:58