一、Reflect
1.Reflect是什么
Reflect是一个内置的JS对象,它提供了一系列方法,可以让开发者通过调用这些方法,访问一些JS底层功能。
由于它类似于其他语言的反射,因此取名为Reflect。
2.可以做什么
使用Reflect可以实现诸如 属性的赋值与取值、调用普通函数、调用构造函数、判断属性是否存在与对象中 等等功能。
有一个重要的理念,在ES5就被提出:减少魔法、让代码更加纯粹
这种理念很大程度上是受到函数式编程的影响
ES6进一步贯彻了这种理念,它认为,对属性内存的控制、原型链的修改、函数的调用等等,这些都属于底层实现,属于一种魔法,因此,需要将它们提取出来,形成一个正常的API,并高度聚合到某个对象中,于是,就造就了Reflect对象。
因此,你可以看到Reflect对象中有很多的API都可以使用过去的某种语法或其他API实现。
3.API
- Reflect.set(target, propertyKey, value): 设置对象target的属性propertyKey的值为value,等同于给对象的属性赋值
- Reflect.get(target, propertyKey): 读取对象target的属性propertyKey,等同于读取对象的属性值
- Reflect.apply(target, thisArgument, argumentsList):调用一个指定的函数,并绑定this和参数列表。等同于函数调用
- Reflect.deleteProperty(target, propertyKey):删除一个对象的属性
- Reflect.defineProperty(target, propertyKey, attributes):类似于Object.defineProperty,不同的是如果配置出现问题,返回false而不是报错
- Reflect.construct(target, argumentsList):用构造函数的方式创建一个对象
- Reflect.has(target, propertyKey): 判断一个对象是否拥有一个属性
其他API:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
这里只是通过实例展示Reflect概念
const obj = {
a: 1,
b: 2
};
Reflect.set(obj, "a", 10);
//等效于
// obj.a = 10;
console.log(Reflect.get(obj, "a"));
//等效于
//console.log(obj.a);
function method(a, b){
console.log("method", a, b);
};
Reflect.apply(method, null, [3, 4]);
//等效于
// method(3, 4);
const obj = {
a: 1,
b: 2
};
Reflect.deleteProperty(obj, "a");
//等效于
// delete obj.a;
二、Proxy代理
代理:提供了修改底层实现的方式,代理不占空间
//代理一个目标对象
//target:目标对象
//handler:是一个普通对象,其中可以重写底层实现
//返回一个代理对象
new Proxy(target, handler)
const obj = {
a: 1,
b: 2
};
const proxy = new Proxy(obj, {
set(target, propertyKey, value) {
// console.log(target, propertyKey, value);
// target[propertyKey] = value;//原来的设置值
Reflect.set(target, propertyKey, value);//利用反射
},
get(target, propertyKey) {
if (Reflect.has(target, propertyKey)) {
return Reflect.get(target, propertyKey);
} else {
return -1;
};
},
has(target, propertyKey) {
return false;
}
});
console.log(proxy);
proxy.a = 10;
console.log(proxy.a);
console.log(proxy.d);//不存在返回-1
console.log("a" in proxy);
三、应用-观察者
有一个对象,是观察者,它用于观察另外一个对象的属性值变化,当属性值变化后会收到一个通知,可能会做一些事。
<div id="container"></div>
<script>
//创建一个观察者
function observer(target) {
const div = document.getElementById("container");
const ob = {};
const props = Object.keys(target);//遍历参数属性
for (const prop of props) {
Object.defineProperty(ob, prop, {
get() {
return target[prop];
},
set(val) {
target[prop] = val;
render();
},
enumerable: true//可遍历
})
}
render();
function render() {
let html = "";
for (const prop of Object.keys(ob)) {
html += `
<p><span>${prop}:</span><span>${ob[prop]}</span></p>
`;
}
div.innerHTML = html;
}
return ob;
}
const target = {
a: 1,
b: 3
};
const obj = observer(target);//执行后target再添加键值将无法实时监控
</script>
利用代理与反射优化
<div id="container"></div>
<script>
//创建一个观察者
function observer(target) {
const div = document.getElementById("container");
const proxy = new Proxy(target, {
set(target, prop, value) {
Reflect.set(target, prop, value);//可以从底层监控新增的数据
render();
},
get(target, prop){
return Reflect.get(target, prop);
}
})
render();
function render() {
let html = "";
for (const prop of Object.keys(target)) {
html += `
<p><span>${prop}:</span><span>${target[prop]}</span></p>
`;
}
div.innerHTML = html;
}
return proxy;
};
const target = {
a: 1,
b: 2
};
const obj = observer(target);
</script>
四、应用-'简化’构造函数
class User {
};
function ConstructorProxy(Class, ...propNames) {
return new Proxy(Class, {
construct(target, argumentsList) {
const obj = Reflect.construct(target, argumentsList)
propNames.forEach((name, i) => {
obj[name] = argumentsList[i];
})
return obj;
}
})
};
const UserProxy = ConstructorProxy(User, "firstName", "lastName", "age");
const obj = new UserProxy("小", "李", 18);
console.log(obj);
class Monster {
};
const MonsterProxy = ConstructorProxy(Monster, "attack", "defence", "hp", "rate", "name")
const m = new MonsterProxy(10, 20, 100, 30, "小怪");
console.log(m);
五、应用-可验证函数
function sum(a, b) {
return a + b;
};
function proxyFunction(func,...argus){
const proxy = new Proxy(func,{
apply(target,thisArgument,argumentsList){
argus.forEach((t,i)=>{
const arg = argumentsList[i];
if(typeof arg !==t){
throw new Error('参数类型不达标!');
}
});
return Reflect.apply(target, thisArgument, argumentsList);
}
})
return proxy;
};
const sumProxy = proxyFunction(sum, "number", "number");
console.log(sumProxy(1, 2));