xinchanghao / one-day-in-the-future

有朝一日,起飞...
2 stars 1 forks source link

Proxy实现一些常用功能 #16

Open pzli opened 4 years ago

pzli commented 4 years ago

节流 handler.apply

const proxy = (func, time) => {
  let previous = new Date(0).getTime();

  let handler = {
    apply(target, context, args){
      let now = new Date().getTime()
      if(now - previous > time) {
        previous = now;
        Reflect.apply(func, context, args)
      }
    }
  }

  return new Proxy(func, handler)
}

const f = ()=>{console.log(new Date().getSeconds())}
const f1 = proxy(f, 2000)

setInterval(() => {
  f1()
}, 1000);

单例 handler.contruct

const proxy = (func, time) => {
  let instance

  let handler = {
    construct(target, args){
      if(!instance) {
        instance = Reflect.construct(func, args);
      }
      return instance
    }
  }

  return new Proxy(func, handler)
}

function Person(name, age) {
  this.name = name;
  this.age = age;
}
const SingletonPerson = proxy(Person)

const p1 = new SingletonPerson('p1', 10)
const p2 = new SingletonPerson('p2', 20)

console.log(p1)
console.log(p2)
console.log(p1 === p2)

监听对象内部属性set(多层级递归)handler.defineProperty handler.get

const proxy = (obj, callback) => {
  let handler = {
    get(target, key){
      try {
        return new Proxy(target[key], handler);
      }catch(e) {
        return Reflect.get(target, key);
      }
    },
    defineProperty(target, key, descriptor) {
      callback();
      return Reflect.defineProperty(target, key, descriptor)
    }
  }

  return new Proxy(obj, handler)
}

let obj = proxy({}, () => console.log('oops'));

obj.a = {}
obj.a.b = 'a'
  1. 这里使用了递归的操作,当需要访问对象的属性时候,会判断代理的对象属性的值仍是一个可以代理的对象就递归的进行代理,否则通过错误捕获执行默认的get操作
  2. 定义了defineProperty的拦截方法,当对这个代理对象的某个属性进行赋值的时候会执行对象内部默认的[[SET]]操作进行赋值,这个操作会间接触发defineProperty这个方法,随后会执行定义的callback函数

这样就实现了无论对象嵌套多少层,只要有属性进行赋值就会触发get方法,对这层对象进行代理,随后触发defineProperty执行callback回调函数