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 入门》