1. 程式人生 > >js庫寫法

js庫寫法

定義 ride 不能 對象 主體 模式 ice direction 探測

前言:

現在javascript庫特別多,其寫法各式各樣,總結幾種我們經常見到的,作為自己知識的積累。而目前版本的 JavaScript 並未提供一種原生的、語言級別的模塊化組織模式,而是將模塊化的方法交由開發者來實現。因此,出現了很多種 JavaScript 模塊化的實現方式,

以 AMD 為例,該規範使用 define 函數來定義模塊。

define(factory(){
    //模塊化
});

模塊模式:

模塊模式使用了 JavaScript 的一個特性,即閉包(Closures)。現今流行的一些 JS 庫中經常見到以下形式的代碼:

;(function (參數) {
  // 模塊代碼
  // return something;
})(參數);

上面的代碼定義了一個匿名函數,並立即調用自己。

也有一些開發者在函數表達式前面加上一個驚嘆號(!)或分號(;),而不是用括號包起來。

!function (參數) {
  // 代碼
  // return something
}(參數);

還有些人喜歡用括號將整個 IIFE 圍起來,這樣就變成了以下的形式:

(function (參數) {
  // 代碼
  // return something
}(參數));

參數輸入:

JavaScript 有一個特性叫做隱式全局變量(implied globals),當使用一個變量名時,JavaScript 解釋器將反向遍歷作用域鏈來查找變量的聲明,如果沒有找到,就假定該變量是全局變量。這種特性使得我們可以在閉包裏隨處引用全局變量,比如 jQuery 或 window。然而,這是一種不好的方式。

考慮模塊的獨立性和封裝,對其它對象的引用應該通過參數來引入。如果模塊內需要使用其它全局對象,應該將這些對象作為參數來顯式引用它們,而非在模塊內直 接引用這些對象的名字。以 jQuery 為例,若在參數中沒有輸入 jQuery 對象就在模塊內直接引用 $ 這個對象,是有出錯的可能的。正確的方式大致應該是這樣的:

;(function ($, w) {
  // $ is jQuery
  // w is window
  // 局部變量及代碼
  // 返回
})(jQuery, window);

模塊輸出(Module Export)

有時我們不只是要使用全局變量,我們也要聲明和輸出模塊中的對象,這可以通過匿名函數的 return 語句來達成,而這也構成了一個完整的模塊模式。

技術分享
var klm= (function () {
    var myklm = {},
        modeval= 1;
 
    function privateMethod() {
        // ...
    }
 
    myklm.moduleProperty = 1;
    myklm.moduleMethod = function () {
        // ...
    };
 
    return myklm ;
}());
技術分享

這段代碼聲明了一個變量 MODULE,它帶有兩個可訪問的屬性:moduleProperty 和 moduleMethod,其它的代碼都封裝在閉包中保持著私有狀態。參考以前提過的參數輸入,我們還可以通過參數引用其它全局變量。

輸出簡單對象:

使用對象直接量來表達 JavaScript 對象是很常見的。比如:var x = { p1: 1, p2: "2", f: function(){ /*... */ } }

技術分享
var Module1 = (function () {
  var private_variable = 1;
  function private_method() { /*...*/ }
 
  var my = {
    property1: 1,
    property2: private_variable,
    method1: private_method,
    method2: function () {
        // ...
    }
  };
  return my;
}());
技術分享

輸出函數:

有時候我們希望返回的並不是一個對象,而是一個函數。有兩種需求要求我們返回一個函數,一種情況是我們需要它是一個函數,比如 jQuery,它是一個函數而不是一個簡單對象;另一種情況是我們需要的是一個“類”而不是一個直接量,之後我們可以用 "new" 來實例它。目前版本的 JavaScript 並沒有專門的“類”定義,但它卻可以通過 function 來表達。

技術分享
var klm= (function () {
  // 私有成員及代碼 ...
 
  return function(name) {
    this.name = name;
    this.bark = function() { /*...*/ }
  };
}());
 
var com = new klm("蒯靈敏");
com.bark();
技術分享

前面已經提到一種形式是輸出對象直接量(Object Literal Notation),而 Revealing Module Pattern 其實就是這種形式,只是做了一些限定。這種模式要求在私有範圍內中定義變量和函數,然後返回一個匿名對象,在該對象中指定要公開的成員。

技術分享
var klm= (function () {
  // 私有變量及函數
  var x = 1;
  function f1() {}
  function f2() {}
 
  return {
    public_method1: f1,
    public_method2: f2
  };
}());
技術分享

擴展:

緊耦合擴展:

有時我們要求在擴展時調用以前已被定義的方法,這也有可能被用於覆蓋已有的方法。這時,對模塊的定義順序是有要求的。

技術分享
var klm = (function (my) {
  var old_moduleMethod = my.moduleMethod;
 
  my.moduleMethod = function () {
    // 方法重載
    // 可通過 old_moduleMethod 調用以前的方法...
  };
  return my;
}(klm));
技術分享

克隆與繼承:

技術分享
var MODULE_TWO = (function (old) {
    var my = {},
        key;
 
    for (key in old) {
        if (old.hasOwnProperty(key)) {
            my[key] = old[key];
        }
    }
 
    var super_moduleMethod = old.moduleMethod;
    my.moduleMethod = function () {
        // override method on the clone, access to super through super_moduleMethod
    };
 
    return my;
}(MODULE));
技術分享

上面代碼再精簡一下:可以使用 Object.create()

技術分享
var MODULE_TWO = (function (old) {
  var my = Object.create(old);
 
  var super_moduleMethod = old.moduleMethod;
  my.moduleMethod = function () {
    // override method ...
  };
 
  return my;
}(MODULE));
技術分享

與其它模塊規範或 JS 庫的適配:

模塊環境探測:

現今,CommonJS Modules 與 AMD 有著廣泛的應用,如果確定 AMD 的 define 是可用的,我們當然可以使用 define 來編寫模塊化的代碼。然而,我們不能假定我們的代碼必然運行於 AMD 環境下,有沒有辦法可以讓我們的代碼?

其實我們只需要在某個地方加上對 CommonJS Modules 與 AMD 的探測並根據探測結果來“註冊”自己就可以了,以上那些模塊模式仍然有用。AMD 定義了 define 函數,我們可以使用 typeof 探測該函數是否已定義。若要更嚴格一點,可以繼續判斷 define.amd 是否有定義。另外,SeaJS 也使用了 define 函數,但和 AMD 的 define 又不太一樣。對於 CommonJS,可以檢查 exports 或是 module.exports 是否有定義。

技術分享
var klm = (function () {
  var my = {};
  // 代碼 ...
 
  if (typeof define == ‘function‘) {
    define( function(){ return my; } );
  }else if (typeof module != ‘undefined‘ && module.exports) {
    module.exports = my;
  }
  return my;
}());
技術分享

其它一些 JS 庫的做法

jquery的檢測方式

技術分享
if ( typeof module === "object" && module && typeof module.exports === "object" ) {
    module.exports = jQuery;
} else {
    if ( typeof define === "function" && define.amd ) {
        define( "jquery", [], function () { return jQuery; } );
    }
}
 
if ( typeof window === "object" && typeof window.document === "object" ) {
    window.jQuery = window.$ = jQuery;
}
技術分享

接下來看看多重匿名函數的做法:

技術分享
(function (root, factory) {
  if (typeof exports === "object" && exports) {
    factory(exports); // CommonJS
  } else {
    var mustache = {};
    factory(mustache);
    if (typeof define === "function" && define.amd) {
      define(mustache); // AMD
    } else {
      root.Mustache = mustache; // <script>
    }
  }
}(this, function (mustache) {
  // 模塊主要的代碼放在這兒
});
技術分享

這段代碼與前面介紹的方式不太一樣,它使用了兩個匿名函數。後面那個函數可以看作是模塊代碼的工廠函數,它是模塊的主體部分。前面那個函數對運行環境進行 檢測,根據檢測的結果對模塊的工廠函數進行調用。另外,作為一個通用庫,它並沒使用 window 對象,而是使用了 this,因為在簡單的函數調用中,this 其實就是全局對象。

再看看 doT 的做法

技術分享
(function() {
    "use strict";
 
    var doT = {
        version: ‘1.0.0‘,
        templateSettings: { /*...*/ },
        template: undefined, //fn, compile template
        compile:  undefined  //fn, for express
    };
 
    if (typeof module !== ‘undefined‘ && module.exports) {
        module.exports = doT;
    } else if (typeof define === ‘function‘ && define.amd) {
        define(function(){return doT;});
    } else {
        (function(){ return this || (0,eval)(‘this‘); }()).doT = doT;
    }
    // ...
}());
技術分享

這段代碼裏的 (0, eval)(‘this‘) 是一個小技巧,這個表達式用來得到 Global 對象,‘this‘ 其實是傳遞給 eval 的參數,但由於 eval 是經由 (0, eval) 這個表達式間接得到的,因此 eval 將會在全局對象作用域中查找 this,結果得到的是全局對象。若是代碼運行於瀏覽器中,那麽得到的其實是 window 對象。

JavaScript 模塊化的未來

尚在制定中的 ES 6 會對模塊作出語言級別的定義。我們來看一個實例,以下的代碼段摘自“ES6:JavaScript中將會有的幾個新東西”

技術分享
module Car { 
  // 內部變量
  var licensePlateNo = ‘556-343‘; 
  // 暴露到外部的變量和函數
  export function drive(speed, direction) { 
    console.log(‘details:‘, speed, direction); 
  } 
  export module engine{ 
    export function check() { } 
  } 
  export var miles = 5000; 
  export var color = ‘silver‘; 
};
技術分享

在以後自己寫js封裝的時候上面是我參考的資料,繼續激情的前進著。。。

js庫寫法