ES6 之 Proxy 和 Reflect

2019-06-17  本文已影响0人  泉泉泉泉泉泉

Proxy

在教程中对于Proxy的定义是这样的,Proxy用于修改某些操作的默认行为,即对编程语言层面进行修改,属于“元编程”,Proxy意思为“代理”,即在访问对象之前建立一道“拦截”,任何访问该对象的操作之前都会通过这道“拦截”,即执行Proxy里面定义的方法。

举个栗子:

var obj = { time: '2018-01-01', name: 'lx', _r: 123 }
var monitor = new Proxy(obj, {
  get(target, key) {  //读取
    return target[key] && target[key].replace('2018', '2017')  //把值的2018改成2017
  },
  set(target, key, value) {  //设置
    if (key === 'name') { return target[key] = value }  //只允许修改name
    else { return target[key] }
  },
  has(target, key) {  //拦截key in object
    if (key === 'name') { return target[key] }
    else { return false }
  },
  deleteProperty(target, key) {  //拦截delete
    if (key.indexOf('_') > -1) { delete target[key]; return true }
    else { target[key] }
  },
  ownKeys(target) {//拦截Object.keys,Objects.getOwnPropertySymbols,Object.getOwnPropertyNames等
    return Object.keys(target).filter(item => item != 'time')
  }
})
//用户看到和操作的是monitor
console.log('get', monitor.time)   //2017-01-01
monitor.time = '2019'
console.log('set', monitor.time)  //2017-01-01
console.log("monitor",monitor)
console.log('has', 'name' in monitor, 'time' in monitor) //true  false
delete monitor.time
console.log('delete', monitor)  //time依然存在于monitor
console.log('ownkeys', Object.keys(monitor));  //,保护monitor不显示time

猜猜应该输出什么内容?

get 2017-01-01
set 2017-01-01
monitor { name: 'lx', _r: 123 }
has true false
delete { name: 'lx', _r: 123 }
ownkeys [ 'name', '_r' ]

Reflect

来看个小栗子:

//旧写法 es5
try {
  Object.defineProperty(target, property, attributes);
  // success
} catch (e) {
  // failure
}
//新写法 - Reflect对象操作 es6
if (Reflect.defineProperty(target, property, attributes)) {
  // success
} else {
  // failure
}

再看个小栗子:

let obj = {
     name:"小明",
     age:15
}
//旧写法
console.log(name in obj);// true 
console.log(Reflect.has(obj,"name"));// true

因为Reflect对象的操作和Proxy对象的操作是一一对应的,所以在Proxy的拦截操作中,可以直接利用Reflect对象直接获取Proxy的默认值。

再举个小栗子:

let target = {
     name:"小明",
     age:15
}
let handler = {     
  get:function(target,name,property){
      if(name === "name"){
             console.log("success");
       }else{
              console.log("success");
       }
        return Reflect.get(target,name,property);
  }
}
let pro = new Proxy(target,handler);
console.log(pro.name)
//结果为
// success
//小明

看了这么多的小栗子,应该明白了Proxy和Reflect之间的关系。

再看个大栗子:

function createValidator(target, validator) {
return new Proxy(target, {
  _validator: validator,
  set(target, key, value, proxy) {
    if (target.hasOwnProperty(key)) {
      let validator = this._validator[key];
      if (!!validator(value)) {
        Reflect.set(target, key, value, proxy)
      } else {
        // console.log(`cannot set ${key} to ${value}`)
        throw Error(`cannot set ${key} to ${value}`)
      }
    } else {
      throw Error(`${key} is not a valid property`)
    }
  }
})
}

const personValidator = {
name(val) { return typeof val === 'string' },
age(num) { return typeof num === 'number' }
}

class Person {
constructor(name, age) {
  this.name = name;
  this.age = age;
  return createValidator(this, personValidator)
}
}
const validator = new Person("zlq", 27)
validator.name = 0
validator.age = 'Bill';  
// validator.age = 0
// validator.name = 'Bill'; 
console.log(validator)

这个栗子的输出的结果是什么呢?你猜猜看!

注意!!!
如果 Proxy对象和 Reflect对象联合使用,前者拦截赋值操作,后者完成赋值的默认行为,而且传入了receiver,那么Reflect.set会触发Proxy.defineProperty拦截。

Reflect对象一共有 13 个静态方法。

Reflect.apply(target, thisArg, args)
Reflect.construct(target, args)
Reflect.get(target, name, receiver)
Reflect.set(target, name, value, receiver)
Reflect.defineProperty(target, name, desc)
Reflect.deleteProperty(target, name)
Reflect.has(target, name)
Reflect.ownKeys(target)
Reflect.isExtensible(target)
Reflect.preventExtensions(target)
Reflect.getOwnPropertyDescriptor(target, name)
Reflect.getPrototypeOf(target)
Reflect.setPrototypeOf(target, prototype)

上面这些方法的作用,大部分与Object对象的同名方法的作用都是相同的,而且它与Proxy对象的方法是一一对应的,就不做赘述,具体每个方式的使用、参数可以参考阮一峰老师的地址:
http://es6.ruanyifeng.com/#docs/reflect

基于Proxy和Reflect的观察者模式实现如下:

//初始化观察者队列
const queuedObservers = new Set();
//将监听函数加入队列
const observe = fun => queuedObservers.add(fun);
//初始化Proxy对象,设置拦截操作
const observable = obj => new Proxy(obj, { set });
function set(target, key, value, receiver) {
  //内部调用对应的 Reflect 方法
  const result = Reflect.set(target, key, value, receiver);
  //额外执行观察者队列
  queuedObservers.forEach(item => item());
  //  return result;
}
const target = {
  name: "小小",
  age: 18
}
const per = observable(target);
function printName() {
  console.log(per.name);
}
function printAge() {
  console.log(per.age)
}
observe(printName);
observe(printAge);
per.name = "小"

输出结果如下

小
18

参考博客:https://www.jianshu.com/p/9e07f182859b

参考博客:https://www.cnblogs.com/lskzj/p/9544958.html

上一篇下一篇

猜你喜欢

热点阅读