JavaScript設計模式

參考書籍Learning Javascript Design Patternshtml

1、設計模式概述與應用場景

首先引用原書的一段話:es6

Patterns are proven solutions: They provide solid approaches to solving issues in software development using proven techniques that reflect the experience and insights the developers that helped define them bring to the pattern.

Patterns can be easily reused: A pattern usually reflects an out of the box solution that can be adapted to suit our own needs. This feature makes them quite robust.express

Patterns can be expressive: When we look at a pattern there’s generally a set structure and vocabulary to the solution presented that can help express rather large solutions quite elegantly.編程

這裏提到了三點:設計模式

  1. 設計模式是開發者定義的
  2. 設計模式必須具備可重用性,必須很好地處理各類異常狀況
  3. 設計模式必須有本身的一套完整表述

光這樣寫可能既抽象又無趣,於是不妨從幾個小點展開說一下。api

設計模式在robustness(魯棒性,我對這個翻譯一直很不滿)上的卓越表現,使得開發者能夠省去一些花在代碼結構組織上的經歷,而更專于于業務邏輯開發,同時這也能夠省去開發者往後重構代碼的不便。安全

設計模式的通用性也是它的泛用性,它不侷限於特定的使用環境,也不侷限於特定的語言,你用C++,C#,Java,Python,JS都能寫,語言是能夠任選的。閉包

同時好的設計模式也能夠有效減少代碼體積。app

設計模式分爲三大類:ide

  1. 創造型設計模式(creational design patterns)
  2. 結構型設計模式(structural design patterns)
  3. 表現型設計模式(behaviorial design patterns)

如下將主要說說第一類設計模式,以後兩類可能會在之後的博客說起。

2、構造者模式

考慮一個最基本的JS對象構造函數:

function Car( model, year, miles ) {
 
  this.model = model;
  this.year = year;
  this.miles = miles;
 
  this.toString = function () {
    return this.model + " has done " + this.miles + " miles";
  };
}

當須要使用這個構造函數構造一個對象時,只要在函數調用以前加上new操做符,JS引擎將會識別出這是一個構造函數調用,這樣,這個函數會默認返回一個this對象,這個對象的原型就是構造函數的prototype,所以,函數中對model,year等屬性的賦值,就是對這個返回的對象進行的各類操做,使得咱們能獲得咱們想要的對象。

此處的構造函數使用this.<prop>的形式添加新屬性,但實際上新屬性的添加有四種方式,除去這一種,還有三種:

  1. 方括號語法,newObject["someKey"] = "Hello World";
  2. Object.defineProperty方法

    Object.defineProperty( newObject, "someKey", {

    value: "for more control of the property's behavior",
    writable: true,
    enumerable: true,
    configurable: true

    });

  3. Object.defineProperties方法

    Object.defineProperties( newObject, {

    "someKey": {

    value: "Hello World",
    writable: true

    },

    "anotherKey": {

    value: "Foo bar",
    writable: false

    }

    });

此處舉的是原文中getter的例子,若要使用setter,可參考原文。

原文此處還提到了在構造函數的原型上定義公有方法的方式,這麼作可使得每次使用構造函數建立對象時不會從新建立一個屬於被建立對象的方法,而是所有使用這個公有方法。

3、模塊化模式

構造者模式的思想很是好,可是在一點上它有所欠缺,即私有變量。在Java中,聲明私有變量能夠採用private關鍵字,限制變量只能被一個類使用,它的後代,它實例化出來的對象都不能訪問這個變量。

這是一種很是重要的編程思想,那麼若是想用JS去實現應該怎麼作呢?原文中舉的例子以IIFE(當即執行函數)爲主,爲了與es6接軌,下面的例子將圍繞es6的module展開。

首先介紹一下es6的module。爲了處理日益增加的js文件形成的命名衝突和安全問題,ECMA引入了module,在module中聲明的變量不會添加到全局做用域中,這樣就能夠避免全局污染,同時module能夠指定須要輸出的變量和方法。這裏只舉一個簡單的導出與導入例子:

// export data
export var color = "red";
export let name = "Nicholas";
export const magicNumber = 7;

// export function
export function sum(num1, num2) {
    return num1 + num1;
}

// export class
export class Rectangle {
    constructor(length, width) {
        this.length = length;
        this.width = width;
    }
}

// this function is private to the module
function subtract(num1, num2) {
    return num1 - num2;
}

// define a function...
function multiply(num1, num2) {
    return num1 * num2;
}

// ...and then export it later
export { multiply };

代碼來自nicolas zakas所著understanding es6中「用模塊封裝代碼」一章,若對es6的模塊化感興趣,可閱讀相關章節。

回到咱們剛剛說的問題上來,爲了實現「私有變量」這個概念,以上的代碼中定義了一個substract方法,由於它沒有被導出,因此此方法僅在此模塊內可用,這樣es6就從標準上實現了私有變量。

原文裏詳細描述了模塊化模式,它主要包裝了公有和私有方法,在上面的例子中,被導出的變量、常量、函數、類都可視爲公有方法。模塊化模式的思想就是導出一部分公有api,而維持在閉包以內的變量私密。

爲了詳細地說明這種思想,咱們仍是舉一段原文的代碼來作說明(es6的module一樣能夠實現相似的效果)

var testModule = (function () {
 
  var counter = 0;
 
  return {
 
    incrementCounter: function () {
      return counter++;
    },
 
    resetCounter: function () {
      console.log( "counter value prior to reset: " + counter );
      counter = 0;
    }
  };
 
})();

請關注這裏的核心var counter = 0,這個私有變量是在導出的公有方法中進行操做的,用戶得到的對象並無辦法直接操做這個私有變量。看到這裏你可能會聯想到閉包,沒錯,它們的思想是相似的。

模塊化模式一樣容許傳入全局變量如$,並對它進行一些操做。

模塊化也是有一些缺點的,最致命的就是私有變量不能被以後添加的方法操做,這對於debug而言絕對是一場噩夢。

瞭解模塊化更多相關請狠狠戳這篇文章

相關文章
相關標籤/搜索