16種JavaScript設計模式(中)

簡介

上文中介紹了學習設計模式前須要瞭解的一些基礎概念和js的基礎模式-原型模式,沒看過的同窗能夠點這裏,本章將介紹如下幾種模式javascript

  • 單例模式
  • 策略模式
  • 代理模式
  • 迭代器模式
  • 發佈訂閱模式
  • 命令模式
  • 組合模式

單例模式

定義:保證一個類只有一個實例,並提供一個訪問他的全局訪問點html

簡介:單例模式是一種經常使用的模式,咱們在屢次引入其餘模塊時,並不須要每次都建立一個新的模塊對象,複用以前建立過的對象不只能減小內存的開銷,同時也能夠體驗共享對象帶來的便利。簡單來講就是使用閉包持久保存函數上一次的執行結果,在以後的調用中直接返回。例如js 中模塊加載的方式:require、import都使用到了該模式vue

例:java

var getSingle = function (fn) { // 建立單例方法
    var result // 經過閉包保存建立過的對象
    return function () {
        return result || (result = fn.apply(this, arguments))
    }
}

var createPerson = getSingle(function (name) {
    return {name: name}
})

var person1 = createPerson('張三')
var person2 = createPerson('李四')

console.log(person1, person2);  // {name: '張三'} {name: '張三'}
複製代碼

進階示例:dom彈窗git

策略模式

定義:定義一系列算法,把他們一個個封裝起來,而且能夠相互替換github

簡介:現實生活中當咱們要達成一個目的的時候一般會有多種方案能夠選擇。好比立刻年末要發年終獎了,公司針對不一樣類型的員工有不一樣的獎金策略,對於表現突出的員工發3個月的工資,通常的員工發2個月的,打醬油的發1個月的,專寫Bug的扣一個月。在這裏 發年終獎 是目的,針對表現不一樣的員工咱們有多種發獎金的策略,可使用策略模式來實現算法

例:設計模式

var strategies = { // 針對不一樣表現的員工定製策略,每一個策略接受同類型的參數返回相同的結果
    S(salary) {
        return salary * 3
    },
    A(salary) {
        return salary * 2
    },
    B(salary) {
        return salary
    },
    C(salary) {
        return -salary
    }
}

var calculateBonus = function (salary, strategy) {
    return strategies[strategy](salary)
}

console.log(calculateBonus(10000, 'S')); // 30000
console.log(calculateBonus(1000, 'C')); // -1000 

複製代碼

進階示例:表單校驗數組

代理模式

定義:當直接訪問一個對象不方便或者不知足須要時,爲其提供一個替身對象來控制對這個對象的訪問瀏覽器

簡介:代理模式是一種很是有意義的模式,在咱們平常開發中有許多經常使用功能均可以經過代理模式實現的,例如 防抖動函數(debounce 經常使用於控制用戶輸入後回調函數觸發的時機),節流函數(throttle 經常使用於控制resize、scroll等事件的觸發頻率),下面咱們實現一個簡單的節流函數

例:

var throttle = function (fn, interval) {
    var firstTime, timer
    return function () {
        var _this = this
        if(!firstTime) {
            fn.apply(this, arguments)
            firstTime = true
        }
        
        if (!timer) {
            timer = setTimeout(function() {
                fn.apply(_this, arguments)
                timer = null
            }, interval);
        }
    }
}

var onScroll = function () {
    console.log('onScroll', Date.now())
}
var throttleOnScroll = throttle(onScroll, 2000)

setInterval(throttleOnScroll, 300) // 每2秒執行一次onScroll函數
複製代碼

進階示例:圖片預加載

迭代器模式

定義:提供一種方法順序訪問一個聚合對象中的各個元素,而要不須要暴露該對象的內部表示

簡介:迭代器模式簡單來講就是將迭代過程從業務邏輯中抽離,簡化開發,其分爲內迭代和外迭代。目前許多語言都已經內置了迭代器的實現,如ES5中的forEach函數就是一種內迭代的實現。

例:

Array.prototype.myEach = function (cb) {
    for (let index = 0; index < this.length; index++) {
        const element = this[index];
        if(cb(element, index) === false) {
            break
        }
        
    }
};

['a','b','c'].myEach(console.log) // a b c
複製代碼

進階示例:外迭代

發佈訂閱模式(劃重點)

定義:分離事件建立者和執行者,執行方只需訂閱感興趣的事件發生點。減小對象間的耦合關係,新的訂閱者出現時沒必要修改原有代碼邏輯

簡介:發佈訂閱模式又叫觀察者模式,它定義了對象間一種一對多的關係,當一個對象的狀態發生改變時,全部依賴於它的對象都將獲得通知。 發佈訂閱模式在咱們平常開發中應用十分普遍,如瀏覽器的dom事件通知機制(document.addEventListener),以及vue框架中數據改變時自動刷新dom的雙向綁定機制都是基於該模式

例:

var Event = function () {
    var clientList = {} // 訂閱者數組

    this.listen = function (key, cb) { // 訂閱方法
        clientList[key] = clientList[key] || []
        clientList[key].push(cb)
    }

    this.remove = function (key, cb) { // 取消訂閱
        var fns = clientList[key]
        if(!cb) {
            clientList[key] = []
        }else if(fns && fns.length) {
            clientList[key] = fns.filter(fn => fn !== cb)
        }
    }

    this.trigger = function () { // 通知訂閱者
        var key = Array.prototype.shift.call(arguments)
        var args = arguments
        var fns = clientList[key]
        var _this = this

        if(fns && fns.length) {
            fns.myEach(function(fn) {
                fn.apply(_this, args)
            })
        }
    }
}

var event = new Event()

event.listen('phone', function getPhone() {
    Array.prototype.unshift.call(arguments, '有個挨千刀的半夜打電話來了他是:')
    console.log.apply(this, arguments)
})

event.trigger('phone', '大狗子') // 有個挨千刀的半夜打電話來了他是:大狗子
event.trigger('phone', '二狗子') // 有個挨千刀的半夜打電話來了他是:二狗子
複製代碼

進階示例:進階版發佈訂閱模式

命令模式

定義:將一組行爲抽象爲對像並提供執行、撤銷等方法,解決它與調用者的之間的耦合關係

簡介:命令模式是對簡單優雅的模式之一,其中「命令」指的是一個執行某些特定事情的指令。該模式適用於須要向某些對象發出請求,但不知道接受者是誰,也不知道要執行哪些操做。例如咱們平時去飯店點菜是咱們並不須要知道這道菜是誰作的怎麼作的,咱們只須要請服務員把需求寫在訂單上就能夠了。

例:

var client = { // 顧客(命令發出者)
    name: '鐵蛋兒'
}
var cook = { // 廚師(命令發執行者)
    makeFood: function (food) {
        console.log('開始作:', food)
    },
    serveFood: function (client) {
        console.log('上菜給:', client.name)
    }    
}

function OrderCommand(receiver, food) { // 命令對象
    this.receiver = receiver
    this.food = food
}

OrderCommand.prototype.execute = function (cook) { // 提供執行方法
    cook.makeFood(this.food)
    cook.serveFood(this.receiver)
}

var command = new OrderCommand(client, '宮保雞丁')
command.execute(cook) // 開始作:宮保雞丁; 上菜給鐵蛋兒

複製代碼

進階示例:控制小球運動

組合模式

定義:將一系列具備相同方法的對象合併成一個具備該方法的組合對象,統一執行

簡介:組合模式將對象組合成樹形結構,以表示「部分-總體」的層次結構。同時利用對象的多態性,使得單個對象的使用和組合對象的使用具備一致性。例如咱們經過命令模式定義了一系列的命令,而且但願組合這些命令造成一個命令宏統一的執行。

例:

// 定義一些命令
var openDoorCommand = {
    execute: function(){
        console.log('開門')
    }
}

var openPcCommand = {
    execute: function(){
        console.log('開電腦')
    }
}

var openLolCommand = {
    execute: function(){
        console.log('擼一局')
    }
}

// 定義命令宏組合命令
var MarcoCommand = {
    list: [],
    add: function (command) {
        this.list.push(command)
    },
    execute: function () {
        this.list.forEach(function(command) {
            command.execute()
        })
    }
}

MarcoCommand.add(openDoorCommand)
MarcoCommand.add(openPcCommand)
MarcoCommand.add(openLolCommand)
MarcoCommand.execute() // 開門 開電腦 擼一局

複製代碼

進階示例:組合命令控制小球運動

感悟

相信看到這裏的同窗會發現其實咱們平時編寫的代碼中已經多多少少用到了一些設計模式,他們並非一些高深複雜的理論知識。掌握一些經常使用的設計模式在幫助咱們提高本身的代碼質量的同時也能幫咱們更好的和同事溝通。

總結

本文中全部進階示例均可以在github.com/ni742015/de…這裏找到,這些示例結合了咱們實際開發中會遇到的一些問題(能夠說是這篇文章的精華了),能加深你對設計模式的理解,但願對你們有幫助。

系列連接

  1. 16種JavaScript設計模式(上)
  2. 16種JavaScript設計模式(中)
  3. 16種JavaScript設計模式(下)還在計劃中。

本文主要參考了《javascript設計模式與開發實踐》一書

相關文章
相關標籤/搜索