Proxy概念
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Proxy和Reflect</title>
</head>
<body>
<script>
//monitor是obj是代理,对monitor的操作最后会映射给obj
{
let obj = {
time: '2019-3-16',
name: 'net',
_r: 123
}
let monitor = new Proxy(obj, {
//拦截对象属性的读取
get (target, key) {
return target[key].replace('2019', '2020');
},
//拦截对象属性的设置
set (target, key, value) {
if (key === 'name') {
return target[key] = value;
} else {
return target[key];
}
}
});
console.log(obj.time);//2019-3-16
console.log(monitor.time);//2020-3-16
monitor.time = '2020';
console.log(monitor.time);//2020-3-16
monitor.name = 'hello';
console.log(monitor.name);//hello
}
//判断当前对象中是否有某个属性key in obj
{
let obj = {
time: '2019-3-16',
name: 'net',
_r: 123
}
let monitor = new Proxy(obj, {
has(target, key) {
if (key === 'name') {
return target[key];
} else {
return false;
}
}
});
console.log('name' in obj);//true
console.log('_r' in obj);//false
}
//拦截删除操作
{
let obj = {
time: '2019-3-16',
name: 'net',
_r: 123
}
let monitor = new Proxy(obj, {
deleteProperty(target, key) {
if (key.indexOf('_') > -1) {
delete target[key];
return true;
} else {
return target[key];
}
}
});
delete monitor.time;
console.log(monitor.time);//2019-3-16
delete monitor._r;
console.log(monitor._r);//undefined
}
//拦截Object.keys Object.getOwnPropertySymbols Object.getOwnPropertyNames
{
let obj = {
time: '2019-3-16',
name: 'net',
_r: 123
}
let monitor = new Proxy(obj, {
ownKeys(target) {
return Object.keys(target).filter(item => item != 'time');
}
});
console.log(Object.keys(monitor));
//(2) ["name", "_r"]
}
</script>
</body>
</html>
Reflect
对象的设计目的
- 将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。也就是说,从Reflect对象上可以拿到语言内部的方法。
- 修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false
- 让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。
- Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。
代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Proxy和Reflect</title>
</head>
<body>
<script>
//Proxy有的方法Reflect都有
{
let obj = {
time: '2019-3-16',
name: 'net',
_r: 123
}
console.log(Reflect.get(obj, 'time'));//2019-3-16
Reflect.set(obj, 'name', 'hello');
console.log(obj.name);//hello
console.log(Reflect.has(obj, 'name'));//true
}
</script>
</body>
</html>
数据类型校验例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Proxy和Reflect</title>
</head>
<body>
<script>
//数据类型校验,实现校验和业务解耦
function validator(target, condition) {
return new Proxy(target, {
_condition: condition,
set (target, key, value, proxy) {
if (target[key]) {
let va = this._condition[key];
if (!!va(value)) {
return Reflect.set(target, key, value, proxy)
} else {
throw Error(`不能设置${ key }为${ value }`);
}
} else {
throw Error(`不存在 ${ key }`);
}
}
});
}
const personCondition = {
name(val) {
return typeof val === 'string';
},
age(val) {
return typeof val === 'number' && val > 18;
}
}
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
return validator(this, personCondition);
}
}
var person = new Person('zhangsan', 30);
console.log(person);
//Proxy {name: "zhangsan", age: 30}
person.name = 22;
//Uncaught Error: 不能设置name为22
person.age = 12;
//Uncaught Error: 不能设置age为12
person.abc = 11;
//Uncaught Error: 不存在 abc
</script>
</body>
</html>