在学习proxy和reflect之前我们先了解一下javascript中的Object。
Object构造函数的属性
object.length 值为1
object.prototype 原型属性,可以为所有Object类型的对象添加属性
object.prototype.constructor 原型构造器,用于创建一个对象的原型
object.prototype.proto 指向实例化对象中的原型,其中Object的proto是null
Object构造函数的所有方法
通过复制一个或多个对象来创建一个新的对象。
使用指定的原型对象和属性创建一个新对象。
给对象添加一个属性并指定该属性的配置。
给对象添加多个属性并分别指定它们的配置。
返回给定对象自身可枚举属性的 [key, value] 数组。
冻结对象:其他代码不能删除或更改任何属性。
Object.getOwnPropertyDescriptor()
返回对象指定的属性配置。
返回一个数组,它包含了指定对象所有的可枚举或不可枚举的属性名。
Object.getOwnPropertySymbols()
返回一个数组,它包含了指定对象自身所有的符号属性。
返回指定对象的原型对象。
比较两个值是否相同。所有 NaN 值都相等(这与==和===不同)。
判断对象是否可扩展。
判断对象是否已经冻结。
判断对象是否已经密封。
返回一个包含所有给定对象自身可枚举属性名称的数组。
防止对象的任何扩展。
防止其他代码删除对象的属性。
设置对象的原型(即内部 [[Prototype]] 属性)。
返回给定对象自身可枚举值的数组。
具体这些方法的使用参考MDN,因为不是本文的重点,就不一一详细介绍了。
对象的属性
属性分为数据属性和访问器属性,每个属性都具有自己的属性描述符。
1.数据属性的描述符
Configurable 配置,表示能否删除修改属性的特性,或者把属性修改为访问器属性。默认true。
Enumerable 枚举,表示能否通过for in循环返回属性,默认为true。
Writable 可写,表示能否修改属性,默认为true。
Value 属性的数据值, 默认为undefined。
2.访问器属性的描述符
Configurable 配置,表示能否删除修改属性的特性,或者把属性修改为访问器属性。默认true。
Enumerable 枚举,表示能否通过for in循环返回属性,默认为true
Get 读取属性值调用的函数,默认为undefined。
Set 设置属性值调用的函数, 默认为undefined。
以上属性配置只能通过Object.defineProperty() 和Object.defineProperties()来设置和修改。
下面进入我们的正题,es6如何通过proxy和reflect操作和修改对象内部的属性。
Proxy
一个 Proxy 对象由两个部分组成: target 、 handler 。在通过 Proxy 构造函数生成实例对象时,需要提供这两个参数。 target 即目标对象, handler 是一个对象,声明了代理 target 的指定行为。
let target = {
name: 'Tom',
age: 24
}
let handler = {
get: function(target, key) {
console.log('getting '+key);
return target[key]; // 不是target.key
},
set: function(target, key, value) {
console.log('setting '+key);
target[key] = value;
}
}
let proxy = new Proxy(target, handler)
proxy.name // 实际执行 handler.get
proxy.age = 25 // 实际执行 handler.set
// getting name
// setting age
// 25
// target 可以为空对象
let targetEpt = {}
let proxyEpt = new Proxy(targetEpt, handler)
// 调用 get 方法,此时目标对象为空,没有 name 属性
proxyEpt.name // getting name
// 调用 set 方法,向目标对象中添加了 name 属性
proxyEpt.name = 'Tom'
// setting name
// "Tom"
// 再次调用 get ,此时已经存在 name 属性
proxyEpt.name
// getting name
// "Tom"
// 通过构造函数新建实例时其实是对目标对象进行了浅拷贝,因此目标对象与代理对象会互相
// 影响
targetEpt)
// {name: "Tom"}
// handler 对象也可以为空,相当于不设置拦截操作,直接访问目标对象
let targetEmpty = {}
let proxyEmpty = new Proxy(targetEmpty,{})
proxyEmpty.name = "Tom"
targetEmpty) // {name: "Tom"}
下面是 Proxy 支持的拦截操作一览,一共 13 种。
- get(target, propKey, receiver):拦截对象属性的读取,比如proxy.foo和proxy['foo']。
- set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = v或proxy['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)。
Reflect
ES6 中将 Object 的一些明显属于语言内部的方法移植到了 Reflect 对象上(当前某些方法会同时存在于 Object 和 Reflect 对象上),未来的新方法会只部署在 Reflect 对象上。
Reflect 对象对某些方法的返回结果进行了修改,使其更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。
Reflect 对象使用函数的方式实现了 Object 的命令式操作。
let exam = {
name: "Tom",
age: 24,
get info(){
return this.name + this.age;
}
}
Reflect.get(exam, 'name'); // "Tom"
// 当 target 对象中存在 name 属性的 getter 方法, getter 方法的 this 会绑定 // receiver
let receiver = {
name: "Jerry",
age: 20
}
Reflect.get(exam, 'info', receiver); // Jerry20
// 当 name 为不存在于 target 对象的属性时,返回 undefined
Reflect.get(exam, 'birth'); // undefined
// 当 target 不是对象时,会报错
Reflect.get(1, 'name'); // TypeError
Reflect对象同样也有 13 个静态方法,这是跟proxy是一一对应的。
- Reflect.apply(target, thisArg, args)
- Reflect.construct(target, args)
- Reflect.get(target, name, receiver)
- Reflect.set(target, name, value, receiver)
- Reflect.defineProperty(target, name, desc)
- Reflect.deleteProperty(target, name)
- Reflect.has(target, name)
- Reflect.ownKeys(target)
- Reflect.isExtensible(target)
- Reflect.preventExtensions(target)
- Reflect.getOwnPropertyDescriptor(target, name)
- Reflect.getPrototypeOf(target)
-
Reflect.setPrototypeOf(target, prototype)
当然proxy和reflect可以产生一些联动,在proxy中可以通过reflect获得对象的默认行为。可以看下以下代码:
var loggedObj = new Proxy(obj, {
get(target, name) {
console.log('get', target, name);
return Reflect.get(target, name);
},
deleteProperty(target, name) {
console.log('delete' + name);
return Reflect.deleteProperty(target, name);
},
has(target, name) {
console.log('has' + name);
return Reflect.has(target, name);
}
});
以上就是es6中的proxy和reflect的全部介绍了,有很多方法需要具体了解可以去 MDN 上具体查看。
参考链接
阮一峰ES6 入门教程
菜鸟课程中的proxy和reflect
对象属性的get和set
javascript中的Object