反射 Reflect
当你见到一个新的API,不明白的时候,就在浏览器打印出来看看它的样子。
反射的概念
Reflect 是一个内置的对象,它提供可拦截JavaScript操作的方法。方法与代理处理程序的方法相同。Reflect 不是一个函数对象,因此它是不可构造的。
new Reflect() //错误的写法
反射的使用
Reflect提供了一些静态方法,静态方法是指只能通过对象自身访问的的方法,这个知识在前面几章讲解过。所有方法的详细解析,前往?Reflect详解?查看。
**静态方法列表:**这么多静态方法,你需要学会的是如何使用它们。
- Reflect.apply() 对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和?Function.prototype.apply()?功能类似。
- Reflect.construct()?对构造函数进行?new?操作,相当于执行?new target(…args)。
- Reflect.defineProperty()?和?Object.defineProperty()?类似。
- Reflect.deleteProperty() 作为函数的?delete 操作符,相当于执行?delete target[name]。
- Reflect.enumerate()?该方法会返回一个包含有目标对象身上所有可枚举的自身字符串属性以及继承字符串属性的迭代器,for…in?操作遍历到的正是这些属性。
- Reflect.get()?获取对象身上某个属性的值,类似于?target[name]。
- Reflect.getOwnPropertyDescriptor()?类似于?Object.getOwnPropertyDescriptor()。
- Reflect.getPrototypeOf()?类似于?Object.getPrototypeOf()。
- Reflect.has()?判断一个对象是否存在某个属性,和?in?运算符?的功能完全相同。
- Reflect.isExtensible()?类似于?Object.isExtensible().
- Reflect.ownKeys()返回一个包含所有自身属性(不包含继承属性)的数组。
- Reflect.preventExtensions()?类似于?Object.preventExtensions()?。返回一个Boolean?。
- Reflect.set() 将值分配给属性的函数。返回一个?Boolean,如果更新成功,则返回true。
- Reflect.setPrototypeOf()?类似于?Object.setPrototypeOf()?。
静态方法的使用:
demo1:使用Reflect.get()获取目标对象指定key的value。
let obj = {
a: 1
};
let s1 = Reflect.get(obj, "a")
console.log(s1) // 1
demo2:使用Reflect.apply给目标函数floor传入指定的参数。
const s2 = Reflect.apply(Math.floor, undefined, [1.75]);
console.log(s2) // 1
进一步理解Reflect
看了上面的例子和方法,我们知道Reflect可以拦截JavaScript代码,包括拦截对象,拦截函数等,然后对拦截到的对象或者函数进行读写等操作。
比如demo1的get()方法,拦截obj对象,然后读取key为a的值。当然,不用反射也可以读取a的值。
再看demo2的apply()方法,这个方法你应该比较了解了,和数组中使用apply不同的是,Reflect.apply()提供了3个参数,第一个参数是反射的函数,后面2个参数才是和数组的apply一致。demo2的例子我们可以理解成是拦截了Math.floor方法,然后传入参数,将返回值赋值给s2,这样我们就能在需要读取这个返回值的时候调用s2。
//数组使用apply
const arr = [1, 2, 3]
function a() {
return Array.concat.apply(null, arguments)
}
const s = a(arr)
console.log(s) // [1, 2 ,3]
其实Reflect的作用和我们下面要讲的Proxy是差不多的。
代理 Proxy
语法
let p = new Proxy(target, handler);
target:一个目标对象(可以是任何类型的对象,包括本机数组,函数,甚至另一个代理)用Proxy来包装。 handler:一个对象,其属性是当执行一个操作时定义代理的行为的函数。
代理的使用
**基础demo:**Proxy的demo有很多,我们只分析基础demo,主要看new Proxy({}, handler)的操作,指定目标obj对象,然后handler对象执行get()操作,get()返回值的判断是,如果name是target目标对象的属性,则返回target[name]的值,否则返回37,最后测试的时候,p.a是对象p的key,所以返回a的value,而p.b不存在,返回37。
const obj = {
a: 10
}
let handler = {
get: function(target, name){
console.log('test: ', target, name)
// test: {"a":10} a
// test: {"a":10} b
return name in target ? target[name] : 37
}
}
let p = new Proxy(obj, handler)
console.log(p.a, p.b) // 10 37
这个例子的作用是拦截目标对象obj,当执行obj的读写操作时,进入handler函数进行判断,如果读取的key不存在,则返回默认值。
var obj = new Proxy({}, {
get : function( target , prop ) {
console.log("我要获取值了");
return target[prop];
},
set : function( target, prop, value) {
console.log("我要设置值了");
target[prop] = value;
}
});
obj.vvvv = 1;
obj.vvvv;
// 我要设置值了
// 我要获取值了
总结
无论是反射还是代理,除了他们使用方法不同之外,他们所作的事情非常相似,都可以理解成拦截某个东西,然后执行某个函数操作,再返回函数操作的结果。
大部分前端在日常业务需求中,几乎很少使用到这2个API。