Reflect基础知识

235 阅读4分钟

一. 概述

  • Reflect对象和Proxy对象都是ES6为了操作对象而提供的新API,Reflect对象的设计目的有:
  1. 将object对象的一些明显属于语言内部的方法放到Reflect对象上。也就是从Reflect对象上可以拿到语言内部的方法
  2. 可以修改某些Object对象方法的返回结果,使其变得更加合理。
  3. 可以让Object对象的操作都变成函数行为。例如:name in obj和delete obj.age可以改为使用Reflect.has(obj,name),Reflect.deleteProperty(obj,age)
  4. Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法,也就是不管Proxy怎么修改默认行为,总是可以在Reflect上获取到默认行为

二. Reflect的静态方法

1. Reflect.get

  • 查找并返回target对象的xxx属性,如果没有该属性,则返回undefined;
  • 参数有三个,target表示目标对象如果参数不是Object则报错,name表示对象的属性,receiver表示内部的this
  • 例子:
  var obj={
   foo:1,
   func:2,
   get sum(){
    return this.foo+this.func;
   }
  }
  console.log(Reflect.get(obj,'foo'));//1
  console.log(Reflect.get(obj,'func'));//2
  console.log(Reflect.get(obj,'sum'));//1+2=3
  
  // 第三个参数为this指向的对象
  var other={foo:22,func:90}
  console.log(Reflect.get(obj,'sum',other));//112,内部的this指向other对象

  // 如果只是调用方法,那么是target对象去调用的,所以属性指的是target对象的属性
  console.log(Reflect.get(obj,'foo',other));//1,内部的this指向target对象
  console.log(Reflect.get(obj,'func',other));//2,内部的this指向target对象

2. Reflect.set

  • Reflect.set方法设置target对象的name属性等于value
  • 有四个参数:target表示目标对象,name表示对象属性名称,value表示属性值,receiver表示绑定target对象内部方法的this为receiver对象!
  var obj={
   foo:3,
   set bar(val){
    console.log(this.foo+val)
    return ;  // set方法只会返回true/false
   }
  }
  var other={foo:9999}
  console.log(Reflect.set(obj,'bar',1));//4
  console.log(Reflect.set(obj,'bar',1,other));//10000

3. Reflect.has

  • Reflect.has方法对应name in obj里面的in运算符,如果第一个参数不是对象,会报错
  // 1. in运算符
  var obj={name:'ww'}
  console.log('name' in obj);//true
  console.log('age' in obj)//false

  //2. Reflect.has
  console.log(Reflect.has(obj,'name'))//true
  console.log(Reflect.has(obj,'age'))//false

4. Reflect.apply

  • Reflect.apply方法等同于Function.prototype.apply.call(func,thisArg,args),用于绑定this对象给func函数,参数为func,thisArg,args;thisArg表示绑定的this对象;Reflect.apply(func,thisArgs,args)相当于func.apply(thisArg,args)
  • 如果要绑定一个函数的this对象,可以写成func.apply(obj,args),但是如果函数定义了自己的apply方法,那么就只能写成Function.prototype.apply.call(fn,obj,args),采用Reflect对象可以简化这种操作
  var args=[1,3,5,11,9,76]
  // 旧的写法 Math.min.apply(Math,args)其实就
    // 相当于把Math.min(...args)内部的this绑定为Math(虽然在该函数不需要)
  console.log(Math.min.apply(Math,args));//1
  console.log(Math.max.apply(Math,args));//76
  
  // 新写法 Reflect.apply(Math.min,Math,args)相当于Math.min.apply(Math,args)
  console.log(Reflect.apply(Math.min,Math,args))//1
  console.log(Reflect.apply(Math.max,Math,args))//76

5. Reflect.defineProperty

  • Reflect.defineProperty方法等同于Object.defineProperty,用于为对象定义属性
  • 经常和Proxy.defineProperty结合
  var p=new Proxy({},{
   defineProperty(target,name,val){
    // console.log(Reflect.defineProperty(target,name,val));//true
    return Reflect.defineProperty(target,name,val)
   }
  })
  p.foo="bar"
  console.log(p.foo);//bar

三. 实现观察者模式

  • 观察者模式就是函数自动观察数据对象,一旦对象有变化,函数就会自动执行
  • 思路;通过observable函数创建一个代理器对象,该observable函数内部监听set操作方法;
  • 在代理器对象person的属性值改变之后,就会触发监听器函数handle;
  • 而handle会遍历执行通过observe函数放入执行栈中的待执行函数,从而在对象的属性值改变同时可以执行多个其他函数.
  var set=new Set();// 集合

  var observe=function(fn){
   return set.add(fn)
  };// 添加值到集合中

  var observable=function(obj){
   return new Proxy(obj,{set:handle});// 返回代理器,监听set操作
  }

  // 监听器
  function handle(target,name,val,receiver){
   var res=Reflect.set(target,name,val,receiver);// 默认行为时的结果
   set.forEach(item => item(name,val));// 执行函数
   return res;
  }

  // 返回代理器对象
  const person = observable({
   name: '张三',
   age: 20
  });

  function print() {
   console.log(`${person.name}, ${person.age}`)
  }

  // set中增加了需要在监听set改变之后执行的函数
  observe(print);  
  console.log(set);//Set(1) {ƒ} 指的是print函数
  person.name = '李四';  // 打印 李四, 20
  
  // 再添加一个函数
  function other(name,val){
   console.log(`other:${name}改为${val}`)
  }
  observe(other);
  person.age=99;// 此时会打印 李四, 99  ;  other:age改为99;