經常使用js設計模式

最近太懶了,今年很久沒有下班後好好學習前端了,回家也不怎麼寫代碼了,找不到剛參加工做第一年的時候,下班後還搞到凌晨1 2點的激情了,好多東西也都快忘記了javascript

因此,決定寫下文章,逼着本身繼續前進。。。先複習下,從設計模式開始前端

1. 單例模式

單例模式的重點在於: 屢次實例化這個對象,都只會被實例一次,後面的實例都會共享第一次。java

class Person () {

  static isInstance () {  // 單例模式核心函數,若是 Person.instance 存在則直接返回,不存在就 實例化 Person 對象並賦值
    if (!Person.instance) {
      Person.instance = new Person()
    }
    return Person.instance
  }

  constructor () {
    console.log(`我被實例化了`)
  }

  age () {
    console.log(`我已經18歲了`)
  }
}

let a = Person.isInstance()     // 我被實例化了
a.age()                         // 我已經18歲了

let b = Person.isInstance()     // 這裏不會從新實例化對象,直接返回 第一次實例化的對象
b.age()                         // 我已經18歲了
複製代碼

2. 發佈訂閱

從一個例子來講明(假設的例子)設計模式

macbook pro 有兩個型號的尺寸,13寸和15寸,在新款電腦價格出來以前,咱們須要一次次去官網查看價格,有沒有什麼辦法讓官方價格一出來,咱們就收到推送呢?數組

分析:咱們要收到推送,首先要先訂閱,好比我是要 13 寸仍是 15 寸,若是要 13 寸,就訂閱 13寸的推送,訂閱好後,官方公佈價格,要能響應這些推送函數

class Computer {
   constructor () {
     this.subList = {} // 保存全部的訂閱
   }
 
   /** * 添加訂閱 * 根據上面的例子,訂閱須要訂閱對象,好比訂閱13寸仍是15寸 * 還須要定義怎麼樣的形式接收到推送,好比郵箱仍是短信 * @param key 訂閱對象 * @param fn 接收形式 */
   addSubscribe (key, fn) {
     // 判斷是否已經有這類訂閱消息,好比訂閱13寸,則全部13寸的訂閱則放入同個數組
     if (!this.sublist[key]) {
       this.sublist[key] = [] // 若是沒有這類訂閱,則爲這類訂閱初始化
     }
     this.sublist[key].push(fn) // 將推送形式存入這類訂閱中
   }
 
   /** * 發佈消息 * 根據上面的例子,發佈的時候須要發佈對象,好比13寸的電腦 * 還須要發佈對象的價格 * @param key 對象 * @param price 價格 */
   publish (key, price) {
     let fns = this.sublist[key] // 拿到這類定義的全部推送形式
     if (!fns || fns.length === 0) { // 若是沒有定義推送形式
       console.log(`尚未訂閱${key}的推送`)
       return
     }
     // 遍歷推送形式數組,推送全部的訂閱
     fns.map((fn) => {
       fn.call(this, price)
     })
   }
 }
 
 // -------------------- 分割線 ---------------------------------------
 
 let shop = new Computer()
 // 訂閱
 shop.addSubscribe('13寸', (price) => {
   console.log(`我是經過短信接收的,13寸的價格爲${price}`)
 })
 
 shop.addSubscribe('13寸', (price) => {
   console.log(`我是經過郵件接收的,13寸的價格爲${price}`)
 })
 
 shop.addSubscribe('15寸', (price) => {
   console.log(`15寸的價格爲${price}`)
 })
 
 // 發佈
 shop.publish('13寸', 12000)
 shop.publish('15寸', 15000)
 
 
 // 輸出
 // 我是經過短信接收的,13寸的價格爲12000
 // 我是經過郵件接收的,13寸的價格爲12000
 // 15寸的價格爲15000
複製代碼

3. Revealing Module (揭示模塊) 模式

揭示模塊模式實際上是 Module模式的稍改進版,由於使用module模式時,必需要切換到對象字面量表示法來讓某種方法變成公有方法。因此揭示模塊模式就是爲了改進這種狀況而出現的。學習

揭示模塊模式可以在私有範圍內簡單定義 "全部的函數和變量",並返回一個匿名對象,它擁有指向私有函數的指針,該函數是他但願展現爲公有的方法。ui

var Car = function () {
    var type = '小汽車';  // 定義私有變量
    function year () {  // 定義私有函數
        return '2017'
    };
    
    var color = 'red';  // 定義共有變量
    function money (num) {  // 共有函數
        console.log('這是輛' + this.color + '的' + type + ', 須要' + num + '人民幣')
    }
    
    // 將暴露的公有指針指向到私有函數和屬性上
    return {
        setColor: color,
        getfun: money
    }
} ();
// 調用
console.log(Car.setColor); // red
Car.getfun(20000);    // 這是輛red的小汽車, 須要20000人民幣
Car.setColor = 'blue';  // 修改公有變量
console.log(Car.setColor); // blue
Car.getfun(15000);    // 這是輛blue的小汽車, 須要15000人民幣
複製代碼

優勢this

該模式可使腳本語法更加一致。在模塊代碼底部,它也會很容易指出哪些函數和變量能夠被公開訪問,從而改善可讀性。spa

缺點

若是一個私有函數引用一個公有函數,在須要打補丁時,公有函數是不能被覆蓋的。這是由於私有函數將繼續引用私有實現,改模式並不適用於公有成員,只適用於函數。

4. 構造器模式

javascript 不支持類的概念,但它支持與對象一塊兒用特殊的 construtor(構造器)函數。經過在構造器前面加 new 關鍵字,告訴 javascript 像使用構造器同樣實例化一個新對象,而且對象成員由該函數定義。

1. 基本構造器模式

// 定義一個Car構造函數
function Car (color, year, money) {
    this.color = color;
    this.year = year;
    this.money = money;
    // 建立一個 toString 方法
    this.toString = function () {
      return 'this car is ' + this.color + ' create ' + this.year + ' need ' + this.money;
    }
}
// 建立 Car實例
var one = new Car('red', 2009, 200000);
var two = new Car('blue', 2016, 120000);
console.log(one.toString());
// this car is red create 2009 need 200000
console.log(two.toString());
// this car is blue create 2016 need 120000
複製代碼

這是一個簡單的構造器模式版本,可是會有一些問題:

  1. 繼承變得困難
  2. 每次新定義對象而使用 Car 構造器時 toString()都會分別從新定義

這是很不理想的,咱們須要 toString() 這樣的函數在全部的 Car 之間共享。因此下面升級一下這個設計模式。

2. 帶原型的 Constructor

js中有一個名爲 prototype 的屬性。調用 js 構造器建立一個對象後,新的對象就會具備構造器原型的全部屬性。經過這種方式,能夠建立多個 Car 對象,並訪問相同的原型。

將上面代碼修改以下:

function Car (color, year, money) {
    this.model = model;
    this.year = year;
    this.money = money;
  }

  Car.prototype.toString = function () {
    return 'this car is ' + this.color + ' create ' + this.year + ' need ' + this.money;
  }
  var one = new Car('red', 2009, 200000);
  var two = new Car('blue', 2016, 120000);
  console.log(one.toString());
  // this car is red create 2009 need 200000
  console.log(two.toString());
  // this car is blue create 2016 need 120000
複製代碼

這樣 toString() 就能在全部的Car對象之間共享。

5. 模塊 (module) 模式

Module 模式最初被定義爲一種在傳統軟件工程中爲類提供私有和共有的封裝方法。

在 javascript 中,Module模式用於進一步模擬類的概念,經過這種方式,可以使一個單獨的對象擁有 共有 / 私有方法和變量,從而屏蔽來自全局做用域的特殊部分。產生的結果是:函數名與頁面上其餘腳本定義的函數衝突的可能性下降。

Module 模式的實現

var Car = (function () {
    // 定義私有變量
    var type = '小汽車';
    // 定義私有函數
    var year = function () {
      return '2017'
    };
    // 返回一個暴露出的共有對象
    return {
      color: 'red',  // 共有變量
      money: function (num) {  // 共有函數
        console.log('這是輛' + this.color + '的' + type + ', 須要' + num + '人民幣')
      }
    }
  })();
  // 調用
  console.log(Car.color); // red
  Car.money(20000);    // 這是輛red的小汽車, 須要20000人民幣
  Car.color = 'blue';  // 修改公有變量
  console.log(Car.color); // blue
  Car.money(15000);    // 這是輛blue的小汽車, 須要15000人民幣
複製代碼

在上面代碼中,咱們定義了一個Car來模擬一個類, Car中定義一個私有變量 type 和一個私有方法 year,這兩個私有成員在 Car 的外部是訪問不到的,只能在這個函數內部被訪問;經過 return 一個對象,將公有的變量 color 和共有的函數 money 暴露在函數外面,可以讓外部修改和訪問。

Module 模式的優勢:

  • 只有咱們的模塊才能享有擁有私有函數的自由。由於他們不會暴露於頁面的其他部分(只會暴露咱們輸出的 API),咱們認爲它們是真正私有的。
  • 鑑於函數每每已申明並命名,在試圖找到有哪些函數拋出異常時,這將使得在調試器中顯示調用堆棧變得更容易。

Module 模式的缺點:

  • 因爲咱們訪問共有和私有成員的方式不一樣,當咱們想改變可見性時,實際咱們必需要修改每個曾經使用過該成員的地方。

------ 分割線-----------------------------------

未完待續。。。

相關文章
相關標籤/搜索