Reflect 反射
- Reflect是什么?
Reflect是一个内置的JS对象,它提供了一系列方法,可以让开发者通过调用这些方法,访问一些JS底层功能
由于它类似于其他语言的反射,因此取名为Reflect
- 它可以做什么?
使用Reflect可以实现诸如 属性的赋值与取值、调用普通函数、调用构造函数、判断属性是否存在与对象中 等等功能
- 这些功能不是已经存在了吗?为什么还需要用Reflect实现一次?
有一个重要的理念,在ES5就被提出:减少魔法、让代码更加纯粹
这种理念很大程度上是受到函数式编程的影响
ES6进一步贯彻了这种理念,它认为,对属性内存的控制、原型链的修改、函数的调用等等,这些都属于底层实现,属于一种魔法,因此,需要将它们提取出来,形成一个正常的API,并高度聚合到某个对象中,于是,就造就了Reflect对象
因此,你可以看到Reflect对象中有很多的API都可以使用过去的某种语法或其他API实现。
- 它里面到底提供了哪些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:developer.mozilla.org/zh-CN/docs/…
<script>
const obj = {
a: 1,
b: 2
}
// obj.a = 10;
Reflect.set(obj, "a", 10);
console.log(Reflect.get(obj, "a"))
</script>
set方法最简单使用,这里用赋值这个简单操作来说明反射的作用。
<script>
function method(a, b){
console.log("method", a, b);
}
// method(3, 4);
// Reflect.apply 简单的方法调用,null 说明不需要更改this的绑定
Reflect.apply(method, null, [3, 4])
const obj = {
a: 1,
b: 2
}
// delete obj.a;
// Reflect.deleteProperty 删除某个对象的property.
Reflect.deleteProperty(obj, "a");
console.log(obj);
function Test(a, b) {
this.a = a;
this.b = b;
}
// const t = new Test(1, 3);
// 下面是使用 Reflect.construct 来同样实现一个new这个关键字是干了什么
const t = Reflect.construct(Test, [1, 3]);
console.log(t)
const obj1 = {
a: 1,
b: 2
}
// console.log("a" in obj);
// 下面是使用 Reflect.has 来实现 属性 in是干什么用的
console.log(Reflect.has(obj1, "a"));
</script>
Proxy 代理
代理:提供了修改底层实现的方式
//代理一个目标对象
//target:目标对象
//handler:是一个普通对象,其中可以重写底层实现
//返回一个代理对象
new Proxy(target, handler)
<script>
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);
console.log("a" in proxy);
</script>
new Proxy(target, handler) 可以实现对目标对象的同样的方法实现外边再包一层的目的。调用的时候直接调用Proxy的方法,Proxy底层会调用目标对象的相关方法。
下面实现的是set方法包的一层
set(target, propertyKey, value) {
// console.log(target, propertyKey, value);
// target[propertyKey] = value;
Reflect.set(target, propertyKey, value);
}
下面是get方法包了一层
get(target, propertyKey) {
if (Reflect.has(target, propertyKey)) {
return Reflect.get(target, propertyKey);
} else {
return -1;
}
},
下面是has方法包了一层
has(target, propertyKey) {
return false;
}
观察者模式
有一个对象,是观察者,它用于观察另外一个对象的属性值变化,当属性值变化后会收到一个通知,可能会做一些事。 其实本质都是监听属性的set方法。
方法1 使用 defineProperty 这个方法去做
下面是具体代码
<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: 2
}
const obj = observer(target)
</script>
方法2 使用 Proxy 这个方法去做
下面是具体的代码
<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>
偷懒的构造函数
<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);
</script>
可验证的函数参数
<script>
function sum(a, b) {
return a + b;
}
function validatorFunction(func, ...types) {
const proxy = new Proxy(func, {
apply(target, thisArgument, argumentsList) {
types.forEach((t, i) => {
const arg = argumentsList[i]
if (typeof arg !== t) {
throw new TypeError(`第${i+1}个参数${argumentsList[i]}不满足类型${t}`);
}
})
return Reflect.apply(target, thisArgument, argumentsList);
}
})
return proxy;
}
const sumProxy = validatorFunction(sum, "number", "number")
console.log(sumProxy(1, 2))
</script>
通用的写法
<script>
function sum(a, b) {
return a + b;
}
function validatorFunction(func, ...types) {
return function(...argumentsList) {
types.forEach((t, i) => {
const arg = argumentsList[i]
if (typeof arg !== t) {
throw new TypeError(`第${i+1}个参数${argumentsList[i]}不满足类型${t}`);
}
})
return func(...argumentsList)
}
return proxy;
}
const sumProxy = validatorFunction(sum, "number", "number")
console.log(sumProxy(1, 2))
</script>