文章目录
- 一、什么是Proxy
- 二、语法
- 三、Proxy 方法
- 1、get() 方法
- 2、set() 方法
- 3、apply() 方法
- 4、has() 方法
- 5、construct() 方法
- 6、deleteProperty() 方法
一、什么是Proxy
Proxy 可以理解成,在目标对象之前
架设一层“拦截
”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
二、语法
var proxy = new Proxy(target, handler);
解释: Proxy 对象的所有用法,都是上面这种形式,不同的只是handler参数的写法。其中,new Proxy()表示生成一个Proxy实例
,target参数
表示所要拦截的目标对象
,handler参数
也是一个对象,用来定制拦截行为
。
三、Proxy 方法
1、get() 方法
get方法用于拦截某个属性的读取操作
,可以接受三个参数,依次为目标对象
、属性名
和 proxy 实例
本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。
语法:
var handler = {
get (target, propKey, ctx) {
return target[propKey];
}
};
例:
var person = {
name: "张三"
};
var proxy = new Proxy(person, {
get: function(target, propKey) {
if (propKey in target) {
return target[propKey];
} else {
throw new ReferenceError("Prop name \"" + propKey + "\" does not exist.");
}
}
});
console.log(proxy.name) // "张三"
console.log(proxy.age) // 抛出一个错误
运行结果:
解释: 上面代码表示,如果访问目标对象不存在的属性,会抛出一个错误。如果没有这个拦截函数,访问不存在的属性,只会返回undefined。
2、set() 方法
set方法用来拦截某个属性的赋值操作
,可以接受四个参数,依次为目标对象
、属性名
、属性值
和 Proxy 实例
本身,其中最后一个参数可选。
语法:
var handler = {
set (target, propKey, propValue, ctx) {
target[propKey] = propValue;
}
};
例:
let validator = {
set: function(obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('The age is not an integer');
}
if (value > 200) {
throw new RangeError('The age seems invalid');
}
}
// 对于满足条件的 age 属性以及其他属性,直接保存
obj[prop] = value;
return true;
}
};
let person = new Proxy({}, validator);
person.age = 100;
console.log(person.age) // 100
person.age = 'young' // 报错
person.age = 300 // 报错
运行结果:
解释: 上面代码中,由于设置了存值函数set,任何不符合要求的age属性赋值,都会抛出一个错误,这是数据验证的一种实现方法。利用set方法,还可以数据绑定,即每当对象发生变化时,会自动更新 DOM。
3、apply() 方法
apply方法拦截函数的调用
、call
和apply
操作。apply方法可以接受三个参数,分别是目标对象
、目标对象的上下文对象
(this)和目标对象的参数数组
。
语法:
var handler = {
apply (target, ctx, args) {
return Reflect.apply(...arguments);
}
};
例:
var target = function () { return 'I am the target'; };
var handler = {
apply: function () {
return 'I am the proxy';
}
};
var p = new Proxy(target, handler);
p()
运行结果:
解释: 上面代码中,变量p是 Proxy 的实例,当它作为函数调用时(p()),就会被apply方法
拦截,返回一个字符串。
4、has() 方法
has()方法用来拦截HasProperty操作
,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in运算符
。has()方法可以接受两个参数,分别是目标对象
、需查询的属性名
。
语法:
var handler = {
has (target, propKey) {
return propKey in target;
}
};
例:
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);
flag = 'prop' in proxy
alert(flag)
运行结果:
解释: 上面代码中,如果原对象的属性名的第一个字符是下划线,proxy.has()就会返回false,从而不会被in运算符发现。
5、construct() 方法
construct()方法用于拦截new命令
,下面是拦截对象的写法。construct()方法可以接受三个参数: 目标对象、构造函数的参数数组、目标对象的上下文对象
(this)
语法:
var handler = {
construct (target, args, ctx) {
return new target(...args);
}
};
例:
const p = new Proxy(function () {}, {
construct: function(target, args) {
console.log('called: ' + args.join(', '));
return { value: args[0] * 10 };
}
});
console.log((new p(1)).value)
运行结果:
解释: construct()方法返回的必须是一个对象,否则会报错。
6、deleteProperty() 方法
deleteProperty方法用于拦截delete操作
,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除。deleteProperty() 方法接受两个参数:目标对象
、目标属性
语法:
var handler = {
deleteProperty (target, propKey) {
delete target[propKey];
}
};
例:
var handler = {
deleteProperty (target, key) {
invariant(key, 'delete');
delete target[key];
return true;
}
};
function invariant (key, action) {
if (key[0] === '_') {
throw new Error(`Invalid attempt to ${action} private "${key}" property`);
}
}
var target = { _prop: 'foo' };
var proxy = new Proxy(target, handler);
delete proxy._prop
// Error: Invalid attempt to delete private "_prop" property
运行结果:
解释: 上面代码中,deleteProperty方法拦截了delete操作符,删除第一个字符为下划线的属性会报错。