乐趣区

关于javascript:浅谈设计模式JS

前阵子看了《JavaScript 设计模式与开发实际》,十分不错的一本书,整顿一些最罕用的设计模式,当前再补充一些其它的。

单例模式

保障一个类仅有一个实例,并且提供一个拜访它的全局拜访点。

实现单例模式

  1. 利用构造函数的实例属性(不通明)

    let CreateDiv = function (name) {
        this.name = name
        this.instance = null
    }
    CreateDiv.prototype.init = function () {let div = document.createElement( 'div')
        // ....
    }
    CreateDiv.creatInstance = function (name) {if (!this.instance) {this.instance = new CreateDiv(name)
        }
        return this.instance
    }
    let lee1 = CreateDiv.creatInstance('lee1')
    let lee2 = CreateDiv.creatInstance('lee2')
    
    lee1 === lee2   // true
  2. 利用闭包(不通明)

    let CreateDiv = function (name) {this.name = name}
    CreateDiv.prototype.init = function () {let div = document.createElement( 'div')
        // ....
    }
    CreateDiv.creatInstance = (function () { // 立刻执行函数
        let instance = null
        return function (name) {if (!instance) {instance = new CreateDiv(name)
            }
            return instance
        }
    })()
    let lee1 = CreateDiv.creatInstance('lee1')
    let lee2 = CreateDiv.creatInstance('lee2')
    
    lee1 === lee2   // true
  3. 单例类(通明)

    下面两种实现形式不通明,须要钻研代码的实现才晓得调 creatInstance 办法,这样不太好啊,我门用一般类来实现

    let Singleton = (function () {
        let instance = null
        let CreateDiv = function (name) {if (instance) return instance
            this.name = name
            this.init() // 创立一个 div
            return instance = this // this 指向新创建的实例对象
        }
        CreateDiv.prototype.init = function () {let div = document.createElement( 'div')
            // ....
        }
        return CreateDiv
    })()
    let lee1 = new Singleton('lee1')
    let lee2 = new Singleton('lee2')
    
    lee1 === lee2   // true
  4. 代理类(代理模式 – 通明)

    // 有一咱们要创立 100 个 div 怎么办 恐怕只能批改 CreateDiv 了吧
    // 代理类只负责单例模式,不影响原来的类,而且也很通明
    let CreateDiv = function (name) {
        this.name = name
        this.init()}
    CreateDiv.prototype.init = function () {let div = document.createElement( 'div')
        // ....
    }
    <!-- 代理类 负责单例模式 不影响 -->
    let ProxyCreatInstance = (function () {
        let instance = null
        return function (name) {if (!instance) {instance = new CreateDiv(name)
            }
            return instance   // 返回了实例
        }
    })()
    let lee1 = new ProxyCreatInstance('lee1') // 间接 new 很通明,创立多个还用 CreateDiv 类
    let lee2 = new ProxyCreatInstance('lee2')
    
    lee1 === lee2   // true
    // 这里波及到了 new 操作符原理,构造函数如果返回了援用类型,会笼罩掉产生的对象
    function mockNew (func, params) {let obj = {}
      obj.__proto__ = func.prototype
      let res = func.call(obj, params)
      return res instanceof Object ? res : obj
      }
  5. js 惰性单例(下面的都混入了传统面向对象语言的特点,没有利用 js 的特点)

    // js 嘛 函数是一等公民,先写个通用的单例函数
    function getSingle (fn) {
        let instance = null
        return function () {return instance || (instance = fn.call(this, arguments)) // fn 有返回 这样写更简洁
        }
    }
    let CreateDiv = function (name) {
        this.name = name
        let div = document.createElement('div')
        div.setAttribute('id', name[0])
        return div
    }
    
    let singleton = getSingle(CreateDiv)
    
    let lee1 = singleton('lee1')
    let lee2 = singleton('lee2')
    
    lee1 === lee2   // true
    
    button.addEventListener('click', function () {let lee = singleton('lee')
    } , false);
    <!-- 惰性单例就是指只在须要的时候才创立 比方我点击按钮后创立 -->
    

小结:不通明模式须要理解代码,不够敌对,基于传统的面向对象,咱们能够通过代理类来实现单例模式的透明化,创建对象和治理单例的职责被散布在两个不同的办法中。惰性单例技术就是指在适合的时候才创建对象。

代理模式

代理模式是为一个对象提供一个代用品或占位符,以便管制对它的拜访。当咱们不不便间接拜访对象本体的时候,通过代理去拜访它。

比方咱们下面单例模式中的代理类 ProxyCreatInstance,咱们如果不必代理,就像第三种单例类一样,代码耦合在一起,如果咱们想创立 100 个 div,只能去更改 CreateDiv 的外部,因为你 new 100 单例模式下个它也只有一个。用了代理模式后,当你须要用单例的时候走代理类就 ok 了,不影响原来的类。

小王要给小芳送花,他不善言辞,送花失败率高,他让小芳的敌人小明替他去送,小明挑了一个小芳开心的日子去送花,一下车替小王解决了终身大事。。。小明这个代理能够监听小芳的情绪,在她情绪好的时候再去送,这就是代理模式的作用。

function Flower () {}

let xiaoWang = {sendFlower: function (target) {let flower = new Flower()
        target.receiveFlower(flower)
    }
}
let xiaoMing =  {receiveFlower: function (flower) {xiaoFang.listenGoodMood(function () {xiaoFang.receiveFlower(flower)   
        }
    })
}
let xiaoFang =  {receiveFlower: function (flower) {console.log('收到花')
    },
    listenGoodMood: function (fn) {setTimeout(fn, 10000)
    }
}

xiaoWang.sendFlower(xiaoMing)

策略模式

定义一系列的算法,将不变的局部和变动的局部隔开,理论就是将算法的应用和实现分离出来,算法的应用形式是不变的

举个栗子

<!-- 年初核算绩效 -->
A 级 奖金为工资 *3
B 级 奖金为工资 *2
C 级 奖金为工资 *1
D 级 奖金为 0

function calculateBonus (performanceLevel, salary){if ( performanceLevel === 'A'){return salary * 3;}
    if (performanceLevel === 'B'){return salary * 2;}
    if (performanceLevel === 'C'){return salary * 1;}
};
calculateBonus('A', 10000);
calculateBonus('B', 5000);

---------------------------------------------
<!-- 这是咱们的策略 -->
const strategies = {'A': function (salary) {return salary * 3;},
    'B': function (salary) {return salary * 2},
    'C': function (salary) {return salary * 1},
    'D': function (salary) {return salary * 0}
}
<!-- 应用办法是不变的 -->
const calculateBonus = function (performanceLevel, salary) {return strategies[performanceLevel](salary)
}

calculateBonus('A', 10000);
calculateBonus('B', 5000);

公布订阅模式

它定义对象间的一种一对多的依赖关系,当一个对象的状 态产生扭转时,所有依赖于它的对象都将失去告诉。例如你订阅了一些情报,情报中心方才公布了一些新音讯,如果外面有你订阅的情报时,就会告诉到你。公布订阅的益处是公布和订阅是齐全解耦的,它们通过音讯类型关联。

像 js 中的 addEventListener、vue 中的 bus 通信都是用的公布订阅模式。

button.addEventListener("click", function () {}, false);
// html
    <button onclick="handleOn('m1')"> 订阅 - 放学啦 </button>
    <button onclick="handleOn('m2')"> 订阅 - 老师来了 </button>
    <button onclick="handleOn('m3')"> 订阅 - 打一把王者 </button>

    <button onclick="handleSub('m1')"> 公布 - 放学啦 </button>
    <button onclick="handleSub('m2')"> 公布 - 老师来了 </button>
    <button onclick="handleSub('m3')"> 公布 - 打一把王者 </button>
// js
  /**
   * 公布订阅模式 公布订阅互不影响,通过音讯类型关联
   */
  var Observe = function () {
    // 定义音讯队列
    this._msgQueue = {}}
  Observe.prototype = {
    // 音讯订阅
    subscribe: function (type, fn, msg) {if (this._msgQueue[type] === void (0)) {this._msgQueue[type] = [fn]
      } else {this._msgQueue[type].push(fn)
      }
      alert(msg)
    },
    // 公布
    publish: function (type, args) {console.log(this._msgQueue[type])
      if (!this._msgQueue[type]) return
      let params = {
        type: type,
        args: args || {}}
      let i = 0, len = this._msgQueue[type].length;
      for (; i < len; i++) {this._msgQueue[type][i].call(this, params)
      }
    },
    // 移除音讯订阅
    off: function (type, fn) {if (this._msgQueue[type] instanceof Array) {let i = 0, len = this._msgQueue[type].length;
        for (; i < len; i++) {if (this._msgQueue[type][i] === fn) {this._msgQueue[type].splice(i, 1)
            return
          }
        }
      }
    }
  }
  // -----------------------------------
  var Ming = new Observe()
  function handleOn(type) {console.log('type', type);
    switch (type) {
      case 'm1':
        console.log('m1');
        let fun1 = function (params) {alert(params.args)
        }
        Ming.subscribe(type, fun1, '放学了 - 订阅胜利')
        break;
      case 'm2':
        let fun2 = function (params) {alert(params.args)
        }
        Ming.subscribe(type, fun2, '老师来了 - 订阅胜利')
        break;
      case 'm3':
        let fun3 = function (params) {alert(params.args)
        }
        Ming.subscribe(type, fun3, '打一把王者 - 订阅胜利')
        break;
    }
  }
  function handleSub(type) {switch (type) {
      case 'm1':
        Ming.publish(type, '放学了,快回家找妈妈')
        break;
      case 'm2':
        let fun2 = function (params) {alert(params.args)
        }
        Ming.publish(type, '老师来了 - 快把手机藏起来')
        break;
      case 'm3':
        let fun3 = function (params) {alert(params.args)
        }
        Ming.publish(type, '同学们请留神,这堂课咱们一起玩王者农药')
        break;
    }
  }

中介者模式

中介者模式的作用就是解除对象与对象之间的紧耦合关系。减少一个中介者对象后,所有的相干对象都通过中介者对象来通信,而不是相互援用,所以当一个对象产生扭转时,只须要告诉中介者对象即可

毛病:零碎中会新增一个中介者对象,减少了对象之间交互的复杂性,中介者对象常常是微小的,有时候难以保护。

退出移动版