4

观察者模式

应用场景:

场景一: 当观察的数据对象发生变化时, 自动调用相应函数。比如 vue 的双向绑定;
场景二: 每当调用对象里的某个方法时, 就会调用相应'访问'逻辑。比如给测试框架赋能的 spy 函数;

场景一: 双向绑定

Object.defineProperty
使用 Object.defineProperty(obj, props, descriptor) 实现观察者模式, 其也是 vue 双向绑定 的核心, 示例如下(当改变 obj 中的 value 的时候, 自动调用相应相关函数):

 var obj ={
    data: {list:[]}
  }
  Object.defineProperty(obj,'list',{
    get(){
      return this.data['list']
    },
    set(val){
      console.log('值被更改了')
      this.data['list'] = val
    }
  })

Proxy
Proxy/Reflect 是 ES6 引入的新特性, 也可以使用其完成观察者模式, 示例如下(效果同上):

   var obj = {
     value: 0
   }
   var proxy = new Proxy(obj,{
     set: function(target,key,value,receiver){
       console.log('调用响应函数')
       Reflect.set(target,key,value,receiver)
     }
   })
   proxy.value = 1

场景二
下面来实现 sinon 框架的 spy 函数:

 const sinon = {
     analyze: {},
     spy:function(obj,fnName){
      const that = this
      const oldFn = Object.getOwnPropertyDescriptor(obj,fnName).value
      Object.defineProperty(obj,fnName,{
        value:function(){
          oldFn()
          if(that.analyze[fnName]){
            that.analyze[fnName].count = ++that.analyze[fnName].count
          }else{
            that.analyze[fnName] = {}
            that.analyze[fnName].count = 1
          }
          console.log(`${fnName}被调用了${that.analyze[fnName].count}`)
        }
      })
     }
   }
   const obj = {
     someFn: function(){
      console.log(`my name is someFn`)
     }
   }
   sinon.spy(obj,'someFn')
   obj.someFn()
  //  my name is someFn
  // someFn被调用了一次
  obj.someFn()
  // my name is someFn
  // someFn 被调用了2次
每当调用对象里的某个方法时, 就会调用相应'访问'逻辑。给测试框架赋能的 spy 函数;

vue 在 3.0 版本上使用 Proxy 重构的原因

首先罗列 Object.defineProperty() 的缺点:

  • Object.defineProperty() 不会监测到数组引用不变的操作(比如 push/pop 等);
  • Object.defineProperty() 只能监测到对象的属性的改变, 即如果有深度嵌套的对象则需要再次给之绑定
  • Object.defineProperty();

关于 Proxy 的优点

  • 可以劫持数组的改变;
  • defineProperty 是对属性的劫持, Proxy 是对对象的劫持;

HappyCodingTop
526 声望847 粉丝

Talk is cheap, show the code!!