ES6看完必会第十二章------ Proxy和Reflect (可私信解惑,不会来捶我)
Proxy 概述
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
下面是常见的 Proxy 支持的拦截操作方法。
(1)get(target, propKey, receiver)
拦截对象属性的读取,比如proxy.foo和proxy['foo']。
最后一个参数receiver是一个对象,可选,参见下面Reflect.get的部分。
get方法用于拦截某个属性的读取操作。上文已经有一个例子,下面是另一个拦截读取操作的例子。
var person = {
name: "jack"
};
var proxy = new Proxy(person, {
get: function(target, property) {
if (property in target) {
return target[property];
} else {
throw new ReferenceError("Property \"" + property + "\" does not exist.");
}
}
});
proxy.name // "jack"
proxy.age // 抛出一个错误
上面代码表示,如果访问目标对象不存在的属性,会抛出一个错误。如果没有这个拦截函数,访问不存在的属性,只会返回undefined。
(2)set(target, propKey, value, receiver)
拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
set方法用来拦截某个属性的赋值操作。
let obj = {
set: function(obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('错误信息:不是整数');
}
if (value > 200) {
throw new RangeError('错误信息:年龄大于200');
}
}
// 对于age以外的属性,直接保存
obj[prop] = value;
}
};
let person = new Proxy({}, obj);
person.age = 13;
person.age // 13
person.age = 'jack' // age不是整数报错
person.age = 300 // age大于200报错
上面代码中,由于设置了存值函数set,任何不符合要求的age属性赋值都会抛出一个错误。
(3)has(target, propKey)
拦截propKey in proxy的操作,以及对象的hasOwnProperty方法,返回一个布尔值。
has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in运算符。
var handler = {
has (target, key) {
if (key[0] === '_') {
return false;
}
return key in target;
}
};
var target = { _prop: 'foo', prop: 'foo' };
var proxy = new Proxy(target, handler);
console.log( '_prop' in proxy ) // false
上面代码中,如果原对象的属性名的第一个字符是下划线,proxy.has就会返回false,从而不会被in运算符发现。
(4)deleteProperty(target, propKey)
拦截delete proxy[propKey]的操作,返回一个布尔值。
deleteProperty方法用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除。
var handler = {
deleteProperty (target, key) {
invariant(key, 'delete');
return true;
}
};
function invariant (key, action) {
if (key[0] === '_') {
throw new Error('删除下划线开头的报错');
}
}
var target = { _prop: 'foo' };
var proxy = new Proxy(target, handler);
delete proxy._prop // 报错内容:删除下划线开头的报错
上面代码中,deleteProperty方法拦截了delete操作符,删除第一个字符为下划线的属性会报错。
(5)apply(target, object, args)
拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
apply方法拦截函数的调用、call和apply操作。
apply方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组。
var twice = {
apply (target, ctx, args) {
return Reflect.apply(...arguments) * 2;
}
};
function sum (num) {
return num + 1 ;
};
var proxy = new Proxy(sum, twice); // 第一个参数相当于原值,第二个参数相当于过滤器,变量proxy相当于过滤后的值
proxy(2) // 6
proxy.call(null, 5, 6) // 22
proxy.apply(null, [7, 8]) // 30
上面代码中,每当执行proxy函数(直接调用或call和apply调用),就会被apply方法拦截。直接调用Reflect.apply方法也会被拦截。
(6)construct(target, args)
拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。
construct方法用于拦截new命令。
construct方法可以接受两个参数。
- target: 目标对象
- args:构建函数的参数对象
var p = new Proxy(function() {}, {
construct: function(target, args) {
console.log('called: ' + args.join(', '));
return { value: args[0] * 10 };
}
});
new p(1).value
// "called: 1"
// 10
construct方法返回的必须是一个对象,否则会报错。
Reflect概述
ES6 中将 Object 的一些明显属于语言内部的方法移植到了 Reflect 对象上(当前某些方法会同时存在于 Object 和 Reflect 对象上),未来的新方法会只部署在 Reflect 对象上。
Reflect 对象对某些方法的返回结果进行了修改,使其更合理。
Reflect 对象使用函数的方式实现了 Object 的命令式操作。
每一个Proxy对象的拦截操作(get、delete、has),内部都调用对应的Reflect方法,保证原生行为能够正常执行。
Reflect方法
Reflect.get(target, name, rec)
查找并返回 target 对象的 name 属性。可以有三个参数,target必须为对象类型,name是target的属性,rec为另一个对象。
let obj = {
name: "jack",
age: 14,
get userInfo(){
return this.name + this.age;
}
}
Reflect.get(obj, 'name'); // "jack"
// 当 target 对象中存在getter 方法, getter 方法的 this 会指向 rec参数的name属性
let receiver = {
name: "haha",
age: 13
}
Reflect.get(obj, 'userInfo', receiver); // haha13
// target 对象不存在该属性,返回 undefined
Reflect.get(obj, 'aaa'); // undefined
// 当 target 不是对象时,会报错
Reflect.get(1, 'name'); // 报错
Reflect.set(target, name, value, receiver)
将 target 的 name 属性设置为 value。返回值为true 表示修改成功,false 表示失败。当 target 为不存在的对象时即报错。
let obj= {
name: "jack",
age: 24,
set userInfo(value){
return this.name = value;
}
}
obj.name; // jack
Reflect.set(obj, 'name', "haha");
obj.name; // haha
// value值为空,表示删除属性
Reflect.set(obj, 'name', );
obj.name; // undefined
// 当 target对象有set方法,setter 方法中的指向receiver, 所以修改的实际上是 receiver 的属性
let receiver = {
name: 8888888888
}
Reflect.set(obj, 'userInfo', "这是修改后的值", receiver);
receiver.name; // 这是修改后的值
上一篇: es6 之 Generator(一)
下一篇: ECMAScript 6 入门-1