关于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;
    }
  }

中介者模式

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

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

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理