Proxy(代理)对象用于自定义JavaScript基本操作的形为(如:属性查找、赋值、枚举、函数调用等),该对象是JavaScript语言标准ES 6(ECMAScript 2015)中新增的对象,通过该对象使我们具有了对JavaScript语言层面进行修改的能力。
1. Proxy语法说明
var p = new Proxy(target, handler);
参数
target-被代理的一个目标对象(可以是任何类型的对象、数组、函数,甚至是另一个代理)handler-处理对象,其属性是定义代理时的行为函数,在目标对象上执行的代理操作都会被些函数处理返回值-代理对象实例
Proxy即“代理”,我们通过new Proxy()构造函数,来对target设置处理对象handler。初始化后会返回一个代理实例对象p,该对象是target的代理器,访问target对象的形为都会首先经过该代理器处理。代理器会判断这种形为有没有在handler中定义,如果已定义则由代理形为处理,如果未定义则由target处理。
2. Proxy.revocable()方法
2.1 revocable()语法
Proxy.revocable(target, handler);
创建一个可撤销的Proxy对象。
可撤销的Proxy对象允有{proxy: proxy, revoke: revoke}两个属性:
proxy-通过调用new Proxy(target, handler)创建的代理对象handler-一个没有参数的函数,用于撤销proxy
revoke()方法调用后,代理会变为无效状态,handler上的任何trap都会返回TypeError。该方法不可重复调用。
2.2 revocable()方法的使用
var revocable = Proxy.revocable({}, {
get: function(target, name) {
return "[[" + name + "]]";
}
});
var proxy = revocable.proxy;
console.log(proxy.foo); // "[[foo]]"
// 撤销代理
revocable.revoke();
console.log(proxy.foo); // 抛出 TypeError
proxy.foo = 1 // 再次抛出 TypeError
delete proxy.foo; // 仍然是 TypeError
typeof proxy // "object"
3. handler对象方法
handler是一个包含陷入(trap)代理的占位符函数。
所有陷入指令都是可选的,如果陷入指令未定义那么会继续定向到target对象来处理。所有陷入指令如下:
handler.getPrototypeOf()-Object.getPrototypeOf的陷入指令(代理)handler.setPrototypeOf()-Object.setPrototypeOf的陷入指令(代理)handler.isExtensible()-Object.isExtensible的陷入指令(代理)handler.preventExtensions()-Object.preventExtensions的陷入指令(代理)handler.getOwnPropertyDescriptor()-Object.getOwnPropertyDescriptor的陷入指令(代理)handler.defineProperty()-Object.defineProperty的陷入指令(代理)handler.has()-in操作符的陷入指令(代理)handler.get()-Object.get的陷入指令(代理)handler.set()-Object.set的陷入指令(代理)handler.deleteProperty()-delete操作符的陷入指令(代理)handler.ownKeys()-Object.getOwnPropertyNames的陷入指令(代理)handler.apply()-function调用的陷入指令(代理)handler.construct()-new操作符(构造函数)的陷入指令(代理)
4. 使用示例
自定义访问器
定义get代理,当用户访问属性不存在时返回'不存在':
var handler = {
get: function(target, name){
return name in target?
target[name] :
'不存在';
}
};
var p = new Proxy({}, handler);
p.a = 1;
p.b = undefined;
console.log(p.a, p.b); // 1, undefined
console.log('c' in p, p.c); // false, '不存在'
当不定义具体代理形为时,形为会继续交由对象本身处理:
var target = {};
var p = new Proxy(target, {});
p.a = 37; // 由对象本身的set处理operation forwarded to the target
console.log(target.a); // 37. 由对象本身的get处理
扩展构造函数
一个函数代理可以很容易地用一个新的构造函数扩展构造函数。扩展构造函数时,需要construct和apply两个处理器:
function extend(sup,base) {
var descriptor = Object.getOwnPropertyDescriptor(
base.prototype,"constructor"
);
base.prototype = Object.create(sup.prototype);
var handler = {
construct: function(target, args) {
var obj = Object.create(base.prototype);
this.apply(target,obj,args);
return obj;
},
apply: function(target, that, args) {
sup.apply(that,args);
base.apply(that,args);
}
};
var proxy = new Proxy(base,handler);
descriptor.value = proxy;
Object.defineProperty(base.prototype, "constructor", descriptor);
return proxy;
}
var Person = function(name){
this.name = name;
};
var Boy = extend(Person, function(name, age) {
this.age = age;
});
Boy.prototype.sex = "M";
var Peter = new Boy("Peter", 13);
console.log(Peter.sex); // "M"
console.log(Peter.name); // "Peter"
console.log(Peter.age); // 13
