反射 Reflect

当你见到一个新的API,不明白的时候,就在浏览器打印出来看看它的样子。

代理(Proxy)和反射(Reflection) - 图1

反射的概念

Reflect 是一个内置的对象,它提供可拦截JavaScript操作的方法。方法与代理处理程序的方法相同。Reflect 不是一个函数对象,因此它是不可构造的。

  1. new Reflect() //错误的写法

反射的使用

Reflect提供了一些静态方法,静态方法是指只能通过对象自身访问的的方法,这个知识在前面几章讲解过。所有方法的详细解析,前往 Reflect详解 查看。

静态方法列表:这么多静态方法,你需要学会的是如何使用它们

1、Reflect.apply()
2、Reflect.construct()
3、Reflect.defineProperty()
4、Reflect.deleteProperty()
5、Reflect.enumerate()
6、Reflect.get()
7、Reflect.getOwnPropertyDescriptor()
8、Reflect.getPrototypeOf()
9、Reflect.has()
10、Reflect.isExtensible()
11、Reflect.ownKeys()
12、Reflect.preventExtensions()
13、Reflect.set()
14、Reflect.setPrototypeOf()

静态方法的使用:

demo1:使用Reflect.get()获取目标对象指定key的value。

  1. let obj = {
  2. a: 1
  3. };
  4. let s1 = Reflect.get(obj, "a")
  5. console.log(s1) // 1

demo2:使用Reflect.apply给目标函数floor传入指定的参数。

  1. const s2 = Reflect.apply(Math.floor, undefined, [1.75]);
  2. 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。

  1. //数组使用apply
  2. const arr = [1, 2, 3]
  3. function a() {
  4. return Array.concat.apply(null, arguments)
  5. }
  6. const s = a(arr)
  7. console.log(s) // [1, 2 ,3]

其实Reflect的作用和我们下面要讲的Proxy是差不多的。

代理 Proxy

Proxy这个词相信你已经听过无数遍了,我曾经写过一篇webpack使用代理来拦截指定域的API请求,转发到新的目标URL的文章 webpack中使用proxy。但是注意Proxy和proxy,大小写字母之间是不同的。本章讲的是大写字母开头的Proxy。

代理(Proxy)和反射(Reflection) - 图2

语法

  1. 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。

  1. const obj = {
  2. a: 10
  3. }
  4. let handler = {
  5. get: function(target, name){
  6. console.log('test: ', target, name)
  7. // test: {"a":10} a
  8. // test: {"a":10} b
  9. return name in target ? target[name] : 37
  10. }
  11. }
  12. let p = new Proxy(obj, handler)
  13. console.log(p.a, p.b) // 10 37

这个例子的作用是拦截目标对象obj,当执行obj的读写操作时,进入handler函数进行判断,如果读取的key不存在,则返回默认值。

我们使用一些http-proxy插件或者webpack的时候,有时候需要访问某个api时,跳转到指定的url,这种方式也能解决跨域访问。这种代理模式和Proxy的代理有异曲同工之妙。但是,别混为一体了。

  1. module.exports = {
  2. devServer: {
  3. proxy: [
  4. {
  5. context: "/api/*", //代理API
  6. target: 'https://www.hyy.com', //目标URL
  7. secure: false
  8. }
  9. ]
  10. }
  11. }

总结

无论是反射还是代理,除了他们使用方法不同之外,他们所作的事情非常相似,都可以理解成拦截某个东西,然后执行某个函数操作,再返回函数操作的结果。

大部分前端在日常业务需求中,几乎很少使用到这2个API,实际使用场景还得在以后的开发中慢慢挖掘。