前言
Proxy对象是ES6(ECMAScript 6)中提供用来操做对象的一个API,其含义官方给出的解释是:Proxy 对象用于定义基本操做的自定义行为(如属性查找,赋值,枚举,函数调用等)
。
es6
Proxy
本意是代理
,那既然代理,确定要具有代理的俩基本素质,1.帮谁代理,2.干点什么。因此, 帮谁代理? 帮目标对象代理
,干点什么?架设拦截,对外界的访问进行过滤和改写
,串起来讲就是:在目标对象以前架设一层“拦截”,外界对该对象的访问,都必须先经过这层拦截,所以提供了一种机制,能够对外界的访问进行过滤和改写
。这下明白了吧,嘿嘿。let proxy = new Proxy(target, handler);
复制代码
tgarget
:要代理的目标对象。(能够是任何类型的对象,包括原生数组,函数,甚至另外一个代理)。
handler
:定义拦截行为的配置对象(也是一个对象,其内部的属性均为执行操做的函数)。数组
含义:拦截对象属性的读取
target
:要代理的目标对象。
key
:属性名。
receiver
:proxy实例(可选参数,通常不用)
bash
const proxy1 = new Proxy({
a: 1
}, {
get(target, key) {
if(Reflect.has(target,key)) {
return Reflect.get(target,key);
}else {
return false;
}
}
})
proxy1.a //1
proxy1.b //false
复制代码
以上,a属性由于有定义,因此可获取到其值为1,b属性没有被定义,输出为false
,若是这里没有定义get方法,那么获取操做就会被转发到目标对象身上,默认若是没有此属性,会返回undefined
,而这里却返回false
,正是由于从新定义了get方法,对返回内容进行了修改,正如其含义,对代理对象属性的读取进行了拦截。app
含义:拦截对象属性的设置
target
:要代理的目标对象。
key
:要设置的属性名。
value
:要设置的属性值。
receiver
:proxy实例(可选参数,通常不用)
函数
const handler = {
set(target,key,value) {
if(!Reflect.has(target,key)) {
Reflect.set(target,key,value);
}else {
console.log(`${key}属性已存在`) //a属性已存在
}
},
get(target,key) {
return Reflect.get(target,key);
}
}
const proxy = new Proxy({},handler);
proxy.a = 1;
console.log(proxy.a);//1
proxy.a = 2;
console.log(proxy.a) //1
复制代码
以上,设置属性的时候若是当前属性已存在,则不能设置成功,所以,咱们能够利用set方法来拦截设置符合咱们指望的属性,根据应用场景,自由发挥。学习
var obj = {};
const target = Object.defineProperty(obj, 'a', {
value: 123,
writable: false,
});
const handler = {
set(target,key,value) {
Reflect.set(target,key,value);
},
get(target, key) {
return Reflect.get(target,key);
}
};
const proxy = new Proxy(target, handler);
proxy.a = 456;
console.log( proxy.a) //123
复制代码
以上,当目标对象的某个属性为不可写状态,那么set方法将会失效。ui
不可写且不可配置
状态,set方法将会失效,但自测发现其结果与configurable
属性的状态并没有关系,只与writable
属性有关,有点小疑惑,但愿看到的大佬能帮小弟解惑。含义:判断对象是否具备某个属性。
target
:要代理的目标对象。
key
:要设置的属性名。
当判断一个对象中是否具备某个属性时,has方法就会生效,典型的操做就是in运算符应用,返回值为布尔类型this
const handler = {
has(target, key) {
if (key[0] === '_') {
console.log('it is a private property')
return false;
}
return key in target;
}
};
const target = {
_a: 123,
a: 456
};
const proxy = new Proxy(target, handler);
console.log('_a' in proxy) // it is a private property false
console.log('a' in proxy);//true
复制代码
以上,当对proxy使用in
运算符时,就会自动触发has方法,若是判断当前属性以_开头的话,就进行拦截,从而不被后面的in
运算符发现,实现隐藏某些特定属性的目的。spa
不过须要注意,当目标对象是不可扩展或者对象的属性是不可配置时has方法不能隐藏目标对象的当前属性,不然拦截会报错。代理
var obj = {
a: 10
};
Object.preventExtensions(obj); //使obj对象不可扩展,也就是使其不能增长新的属性
var p = new Proxy(obj, {
has: function (target, key) {
return false;
}
});
'a' in p // Uncaught TypeError: 'has' on proxy: trap returned falsish for property 'a' but the proxy target is not extensible
let obj = {};
Object.defineProperty(obj, "a", {
configurable: false, //当前属性不可配置
value: 10,
});
var p = new Proxy(obj, {
has: function (target, key) {
return false;
}
});
'a' in p // Uncaught TypeError: 'has' on proxy: trap returned falsish for property 'a' which exists in the proxy target as non-configurable
复制代码
含义:拦截函数的调用、call和apply操做
target
:目标对象。
thisArgs
:目标对象的上下文对象(this)。
args
:目标对象的参数数组。
const handler = {
apply(target, ctx, args) {
return Reflect.apply(...arguments) * 2;
//或者
return target(...args) * 2
}
};
function sum(...args) {
let num = 0;
args.forEach((item) => {
num += item;
})
return num;
}
var proxy = new Proxy(sum, handler);
proxy(1, 2) // 6
proxy.call(null, 5, 6) // 22
proxy.apply(null, [7, 8]) // 30
复制代码
以上,目标对象这里是sum
,必须是一个函数,不然调用会报错。每当proxy函数被直接调用或者call
,apply
方式调用都会当即触发apply
方法,从而该调用被apply
方法拦截,这样就能够利用拦截修改返回值,若是不写apply方法,默认调用sum方法,返回 结果。
含义:拦截new命令
target
:目标对象。
args
:构造函数的参数列表。
newTarget
:建立实例对象时,new命令做用的构造函数(下面例子的p)。
var p = new Proxy(function () {}, {
construct: function (target, args, newTarget) {
console.log('called: ' + args.join(', '));
return {
value: args[0] * 10
};
// return 1 //Uncaught TypeError: 'construct' on proxy: trap returned non-object ('1')
}
});
(new p(1)).value
// "called: 1"
// 10
复制代码
以上,代理的目标对象必须是一个构造函数(只有构造函数才可使用new操做符),当执行new p(1)
时,会马上触发construct
函数,也就是会被该函数拦截,这里的返回值有讲究,必须返回对象,不然会报错。construct
方法执行完毕,p实例也就初始化完成了。
const target = {
foo: function () {
console.log(this === proxy);
}
};
const handler = {};
const proxy = new Proxy(target, handler);
target.foo() // false
proxy.foo() // true
复制代码
能够看出,当目标对象一旦被proxy
代理后,其内部this
就会指向proxy
,而非自身,所以须要注意这点。
好啦,今天关于Proxy的介绍就到这了,以上也只是针对几个比较经常使用的操做函数进行了举例说明,若是想了解更多,建议参考如下资料,如有错误,还望各位大神指正!
阮一峰:ECMAScript 6 入门-Proxy
阮一峰:ECMAScript 6 入门-Reflect
Proxy - JavaScript | MDN