参考:http://es6.ruanyifeng.com/#docs/proxy
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)
,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而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)
。
get:
/**
* 1.get(target, propKey, receiver)
* target:目标对象
* propKey:属性名
* receiver:操作行为所针对的对象(可选)
*
* get方法用于拦截某个属性的读取操作
*/
var student = {
name : "Bill",
age : 20
};
var handlerStudent = {
get: function(target, property, receiver){
if(property in target){
//这里的property = "name",所以不能以target.property的方式调用
return target[property];
}else{
// return "No this property!";
throw "Error";
}
}
};
try{
var getProxy = new Proxy(student, handlerStudent);
console.log(getProxy.name); //Bill
console.log(getProxy.age); //20
console.log(getProxy.sex); //抛出异常
}catch(error){
console.log(error);
}
set:
/**
* 2.set(target, propKey, value, receiver)
* target:目标对象
* propKey:属性名
* value:属性值
* receiver:操作行为所针对的对象(可选)
*
* set方法用于拦截某个属性的赋值操作
*/
var handlerStudent = {
set: function(target, property, value){
if(property === "age"){
if(!Number.isInteger(value)){
throw "TypeError";
}
if(value >= 20){
throw "Value is invalid";
}
}
//这里的property = "age",所以不能以target.property的方式调用
target[property] = value;
}
};
try{
var setProxy = new Proxy({}, handlerStudent);
setProxy.age = 10;
console.log(setProxy.age);
setProxy.age = 'Ac'; //TypeError
setProxy.age = 1234; //Value is invalid
}catch(error){
console.log(error);
}
apply:
/**
* 3.apply(target, object, args)
* target:目标对象
* object:目标对象的上下文
* args:目标对象的数组
*
* apply方法拦截函数的调用,call和apply操作。
*/
function sum(a, b) {
return a + b;
}
var handler = {
apply(target, obj, args){
let a = args.length;
let b = 1;
for(let i = 0; i < a; i++){
b *= args[i];
}
return b;
}
/**
* 你可能会遇到这种写法:
* apply: function(target, obj, args){}
* 不必担心,这与apply(target, obj, args){}是等价的。
*/
}
var proxy = new Proxy(sum, handler);
console.log(proxy(1, 2)); //2
/**
* 为什么call()和apply传参的方式不同?请查阅相关的方法。
*/
console.log(proxy.call(null, 5, 7)); //35
console.log(proxy.apply(null, [10, 20]));//200
has:
/**
* 4.has(target, propKey)
* target:目标对象
* propKey:属性名
*
* has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法就会生效。典型的操作符就是in操作符。
*/
var stu1 = {
name : 'Alice',
score: 59
};
var stu2 = {
name : 'Bill',
score: 86
};
var handler = {
has(target, prop){
if(prop === 'score' && target[prop] < 60){
console.log(target.name + " 不及格");
return false;
}
return prop in target;//这返回的是一个布尔值
}
}
var proxy1 = new Proxy(stu1, handler);
var proxy2 = new Proxy(stu2, handler);
'score' in proxy1; //Alice 不及格
'score' in proxy2; //<啥也没输出>不过你可以添加一个console.log();查看结果
/**
* 即使for(...in...)中有in操作符,但是,has对其不生效。
*/
for(let i in proxy1){
console.log(proxy1[i]);// alice <这里有一个换行> 59
}
for(let i in proxy2){
console.log(proxy2[i]);//Bill <这里有一个换行> 86
}
constract:
/**
* 5.construct(target, ages)
* target:目标对象
* args:构建函数的参数对象
*
* construct方法用于拦截new命令,返回的必须是一个对象
*/
var handler = {
construct(target, args){
args.map(x => console.log(x));//3<这里有一个换行>4<这里有一个换行>5
return { num : args[0] * 10};
}
};
var proxy = new Proxy(function(){}, handler);
/**
* 这里用new proxy()的原因是,返回的是一个对象,需要实例化才能调用其中的num(属性名)的属性值
*/
console.log((new proxy(3, 4, 5)).num);//30
deleteProperty:
/**
* 6.deleteProperty(target, propKey)
* target:目标对象
* propKey:属性名
*
* deleteProperty方法用于拦截delete操作
*/
var handler = {
/**
* deleteProperty无法删除不可配置的属性,否则会报错
*/
deleteProperty (target, propKey){
if(propKey[0] === '_'){
throw new Error("can't delete the property!");
}
return true;
}
}
var target = {_prop: 'a'};
var proxy = new Proxy(target, handler);
try{
delete proxy._prop;
}catch(error){
console.log(error);
}
defineProperty:
/**
* 7.defineProperty(target, propKey, propDesc):
* target:目标对象
* propKey:属性名
* propDesc:属性描述符
*
* defineProperty方法用于拦截Object.defineProperty操作
*/
var handler = {
defineProperty (target, propKey, propDesc){
return false;
}
};
//如果目标对象不可扩展,则defineProperty
不能增加目标对象上不存在的属性,否则会报错。
//另外,如果目标对象的某个属性不可写或不可配置,
//则defineProperty
方法不得改变这两个设置。
var proxy = new Proxy({foo1 : 2}, handler);
proxy.foo = 'A';
console.log(proxy.foo + " " + proxy.foo1);//undefined 2
getOwnPropertyDescriptor:
/**
* 8.getOwnPropertyDescriptor(target, propKey)
* target:目标对象
* propKey:属性名
*
* getOwnPropertyDescriptor方法拦截Object.getOwnPropertyDescriptor(),返回一个属性描述
* 对象或者undefined.
*/
var handler = {
getOwnPropertyDescriptor(target, propKey){
if(propKey[0] === '_'){
return;
}
return Object.getOwnPropertyDescriptor(target, propKey);
}
};
var target = { _foo: 'bar', baz: 'tar'};
var proxy = new Proxy(target, handler);
console.log(Object.getOwnPropertyDescriptor(proxy, 'wat'));
//undefine
console.log(Object.getOwnPropertyDescriptor(proxy, '_foo'));
//undefine(对属性首字符为下划线的直接返回undefine)
console.log(Object.getOwnPropertyDescriptor(proxy, 'baz'));
//{value: "tar", writable: true, enumerable: true, configurable: true}
getPrototypeOf(target):
/**
* 9.getPrototypeOf(target):
* target:目标对象
*
* getPrototypeOf方法用于拦截获取对象原型。可以用于拦截以下操作:
* -Object.prototype.__proto__
* -Object.prototypr.isPrototypeOf()
* -Object.getPrototyprOf()
* -Reflect.getPrototyprOf()
* -instanceof()
*/
var a = {foo : 1}
var proxy = new Proxy({},{
getPrototypeOf(target) {
//返回值必须是对象或者是null。如果目标对象不可扩展,getPrototype方法必须返回目标对象的原型对象
return a;
}
})
console.log(Object.getPrototypeOf(proxy) === a);//true
isExtensible:
/**
* 10.isExtensible(target)
* target:目标对象
*
* isExtensible方法拦截Object.isExtensible操作
*/
var proxy = new Proxy({}, {
isExtensible: function(target) {
//只能返回布尔值,否则将被自动转换为布尔值
return true;
}
});
//返回值必须与目标对象的isExtensible属性保持一致,否则报错
console.log(Object.isExtensible(proxy));
ownKeys:
/**
* 11.ownKeys(target)
* target:目标对象
*
* ownKeys方法用于拦截对象自身属性的读取操作
* -Object.getOwnPropertyNames()
* -Object.getOwnPropertySybols()
* -Object.keys()
*/
var target = {
a : 1,
b : 2,
c : 3
};
var handler = {
ownKeys(target){
return ['a'];
}
}
//这里只举例一个操作,其余的请见最上方的参考
console.log(Object.keys(target));//["a", "b", "c"]
console.log(Object.keys(new Proxy(target, handler)));//["a"]
preventExtensions:
/**
* 12.preventExtensions(target)
* target:目标对象
*
* preventExtensions方法拦截Object.preventExtensions()
* 该方法只有一个限制,即目标对象不可扩展时(即Object.isExtensions为false),
* 其余该方法将返回true,否则报错
*/
var proxy = new Proxy({}, {
preventExtensions(target){
//通过调用一次Object.preventExtensions()来防止Object.isExtensible()返回true
Object.preventExtensions(target);
return true;
}
});
console.log(Object.preventExtensions(proxy));//proxy{}
setPrototypeOf:
/**
* 13.setPrototypeOf(target, proto)
* target:目标对象
* proto:原型对象
*
* setPrototypeOf方法用于拦截Object.setPrototypeOf
*/
var handler = {
setPrototypeOf(target, proto){
throw new Error('error');
}
}
var proto = {};
var target = {};
var proxy = new Proxy(target, handler);
try{
console.log(Object.setPrototypeOf(proxy, proto));
}catch(error){
console.log(error);
}
注:任何细节请前往最上方的网站查看。