成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

淺談 JavaScript 模塊化編程

wdzgege / 2009人閱讀

摘要:與在模塊化編程的世界中,有兩個(gè)規(guī)范不得不提,它們分別是和。所有依賴(lài)于某個(gè)模塊的代碼全部移到模塊加載語(yǔ)句的回調(diào)函數(shù)中去。的語(yǔ)句接受兩個(gè)參數(shù)在回調(diào)函數(shù)中,可以通過(guò)變量引用模塊?;卣{(diào)函數(shù)的返回值就是當(dāng)前對(duì)象的導(dǎo)出值。

JavaScript本身不是一種模塊化語(yǔ)言,設(shè)計(jì)者在創(chuàng)造JavaScript之初應(yīng)該也沒(méi)有想到這么一個(gè)腳本語(yǔ)言的作用領(lǐng)域會(huì)越來(lái)越大。以前一個(gè)頁(yè)面的JS代碼再多也不會(huì)多到哪兒去,而現(xiàn)在隨著越來(lái)越多的JavaScript庫(kù)和框架的出現(xiàn),Single-page App的流行以及Node.js的迅猛發(fā)展,如果我們還不對(duì)自己的JS代碼進(jìn)行一些模塊化的組織的話(huà),開(kāi)發(fā)過(guò)程會(huì)越來(lái)越困難,運(yùn)行性能也會(huì)越來(lái)越低。因此,了解JS模塊化編程是非常重要的。

簡(jiǎn)單的模塊

什么是模塊?我認(rèn)為將不同功能的函數(shù)放在一起,組成一個(gè)能實(shí)現(xiàn)某種或某些特定功能的整體就是一個(gè)模塊,因此這樣:

function add(a, b) {
  return a + b;
}

function divide(a, b) {
  return a / b;
}

如此簡(jiǎn)單的兩個(gè)函數(shù)就可以組成一個(gè)模塊,這個(gè)模塊可以進(jìn)行一些數(shù)學(xué)運(yùn)算。

當(dāng)然沒(méi)有人會(huì)這么寫(xiě)模塊。僅僅是從“型”上來(lái)看,兩個(gè)函數(shù)分散在全局環(huán)境中,這也看不出模塊的特點(diǎn)。模塊存在于全局變量中,應(yīng)該提供一個(gè)命名空間,成為模塊內(nèi)容的入口。那么我們可以將函數(shù)包裹在一個(gè)對(duì)象中:

var math = {
  add: function(a, b) {
    return a + b;
  },
  divide: function(a, b) {
    return a / b;
  }
}

這樣看起來(lái)似乎有模塊的“型”了。但是這樣還不完善,math中的所有成員都是對(duì)外暴露的,如果其中有一些變量不希望被修改的話(huà)那就有風(fēng)險(xiǎn)了。為了防止世界被破壞,為了維護(hù)私有變量不被修改,我們可以使用閉包。

var math = (function() {
  var _flag = 0;

  return {
    add: function(a, b) {
      return a + b;
    },
    divide: function(a, b) {
      return a / b;
    }
  };
})();

外部代碼只能訪問(wèn)返回的adddivide方法,內(nèi)部的_flag變量是不能訪問(wèn)的。關(guān)于創(chuàng)建對(duì)象的一些方法的解釋?zhuān)梢詤⒖嘉业牧硪黄┪?,里面有較詳細(xì)的解釋。

利用自執(zhí)行函數(shù)的特點(diǎn),我們還可以很方便地為模塊添加方法:

var math = (function(module) {
  module.subtract = function(a, b) {
    return a - b;
  }
})(math);

模塊在全局變量中的名稱(chēng)可能會(huì)與其他的模塊產(chǎn)生沖突,例如$符號(hào),雖然使用方便,但多個(gè)模塊可能都會(huì)用它作為自己的簡(jiǎn)寫(xiě),例如jQuery。我們可以在模塊的組織代碼中用$作為形參,將模塊的全名變量作為參數(shù)傳入,可起到防沖突的效果。

var math = (function($) {
  // 這里的$指的就是Math
})(math);

模塊的構(gòu)建思想便是通過(guò)這樣的方式逐漸演化而來(lái),下面將通過(guò)介紹一些JS模塊化編程的標(biāo)準(zhǔn)來(lái)展示如何組織,管理和編寫(xiě)模塊。

AMDCMD

在JavaScript模塊化編程的世界中,有兩個(gè)規(guī)范不得不提,它們分別是AMD和CMD?,F(xiàn)在的JS庫(kù)或框架,凡是模塊化的,一般都是遵循了這兩個(gè)規(guī)范其中之一。

AMD(Asynchronous Module Definition)

CommonJS
在說(shuō)AMD之前,先要提一下CommonJS。CommonJS是為了彌補(bǔ)JavaScript標(biāo)準(zhǔn)庫(kù)過(guò)少的缺點(diǎn)而產(chǎn)生的,由于JS沒(méi)有模塊機(jī)制(ES6引入了模塊系統(tǒng),但瀏覽器全面支持估計(jì)還有好幾年),CommonJS就幫助JS實(shí)現(xiàn)模塊的功能。現(xiàn)在很熱門(mén)的Node.js就是CommonJS規(guī)范的一個(gè)實(shí)現(xiàn)。

CommonJS在模塊中定義方法要借助一個(gè)全局變量exports,它用來(lái)生成當(dāng)前模塊的API:

/* math module */

exports.add = function(a, b) {
  return a + b;
};

要加載模塊就要使用CommonJS的一個(gè)全局方法require()。加載之前實(shí)現(xiàn)的math模塊像這樣:

var math = require("math");

加載后math變量就是這個(gè)模塊對(duì)象的一個(gè)引用,要調(diào)用模塊中的方法就像調(diào)用普通對(duì)象的方法一樣了:

var math = require("math");
math.add(1, 3);

總之,CommonJS就是一個(gè)模塊加載器,可以方便地對(duì)JavaScript代碼進(jìn)行模塊化管理。但它也有缺點(diǎn),它在設(shè)計(jì)之初并沒(méi)有完全為瀏覽器環(huán)境考慮,瀏覽器環(huán)境的特點(diǎn)是所有的資源,不考慮本地緩存的因素,都需要從服務(wù)器端加載,加載的速度取決于網(wǎng)絡(luò)速度,而CommonJS的模塊加載過(guò)程是同步阻塞的。也就是說(shuō)如果math模塊體積很大,網(wǎng)速又不好的時(shí)候,整個(gè)程序便會(huì)停止,等待模塊加載完成。

隨著瀏覽器端JS資源的體積越來(lái)越龐大,阻塞給體驗(yàn)帶來(lái)的不良影響也越來(lái)越嚴(yán)重,終于從,在CommonJS社區(qū)中有了不同的聲音,AMD規(guī)范誕生了。

AMD
它的特點(diǎn)便是異步加載,模塊的加載不會(huì)影響其他代碼的運(yùn)行。所有依賴(lài)于某個(gè)模塊的代碼全部移到模塊加載語(yǔ)句的回調(diào)函數(shù)中去。AMD的require()語(yǔ)句接受兩個(gè)參數(shù):

// require([module], callback)
require(["math"], function(math) {
  math.add(1, 3);
});

在回調(diào)函數(shù)中,可以通過(guò)math變量引用模塊。

AMD規(guī)范也規(guī)定了模塊的定義規(guī)則,使用define()函數(shù)。

define(id?, dependencies?, factory);

它接受三個(gè)參數(shù):
id
這是一個(gè)可選參數(shù),相當(dāng)于模塊的名字,加載器可通過(guò)id名加載對(duì)應(yīng)的模塊。如果沒(méi)有提供id,加載器會(huì)將模塊文件名作為默認(rèn)id。

dependencies
可選,接受一個(gè)數(shù)組參數(shù),傳入當(dāng)前對(duì)象依賴(lài)的對(duì)象id。

factory
回調(diào)函數(shù),在依賴(lài)模塊加載完成后會(huì)調(diào)用,它的參數(shù)是所有依賴(lài)模塊的引用?;卣{(diào)函數(shù)的返回值就是當(dāng)前對(duì)象的導(dǎo)出值。

用AMD規(guī)范實(shí)現(xiàn)一個(gè)簡(jiǎn)單的模塊可以這樣:

define("foo", ["math"], function(math) {
  return {
    increase: function(x) {
      return math.add(x, 1);
    }
  };
});

如果省去id和dependencies參數(shù)的話(huà),就是一個(gè)完全的匿名模塊。factory的參數(shù)將為默認(rèn)值require,exportsmodule加載器將完全通過(guò)文件路徑的方式加載模塊,同時(shí)如果有依賴(lài)模塊的話(huà)可通過(guò)require方法加載。

define(function(require, exports, module) {
  var math = require("math");

  exports.increase = function(x) {
    return math(x, 1);
  };
});

AMD規(guī)范也允許對(duì)加載進(jìn)行一些配置,配置選項(xiàng)不是必須的,但靈活更改配置,會(huì)給開(kāi)發(fā)帶來(lái)一些方便。

baseUrl 以字符串形式規(guī)定根目錄的路徑,以后在加載模塊時(shí)都會(huì)以該路徑為標(biāo)準(zhǔn)。在瀏覽器中,工作目錄的路徑就是運(yùn)行腳本的網(wǎng)頁(yè)所在的路徑。

{
  baseUrl: "./foo/bar"
}

path 可以指定需加載模塊的路徑,模塊名與路徑以鍵-值對(duì)的方式寫(xiě)在對(duì)象中。如果一個(gè)模塊有多個(gè)可選地址,可以將這些地址寫(xiě)在一個(gè)數(shù)組中。

{
  path: {
    "foo": "./bar"
  }
}

關(guān)于模塊路徑的設(shè)置項(xiàng)還有packagesmap。

shim
對(duì)于某些沒(méi)有按照AMD規(guī)范編寫(xiě)的模塊,比如jQuery,來(lái)說(shuō),要使它們能被加載器加載,需要用shim方法為其配置一些屬性。在main模塊中,用require.config()方法:

require.config({
  shim: {
    "jquery": {
      exports: "$"
    },
    "foo": {
      deps: [
        "bar",
        "jquery"
      ],
      exports: "foo"
    }
  }
});

之后再用加載器加載就可以了。

目前實(shí)現(xiàn)了AMD規(guī)范的庫(kù)有很多,比較有名的是Require.js。

CMD(Common Module Definition)

CMD在很多地方和AMD有相似之處,在這里我只說(shuō)兩者的不同點(diǎn)。

首先,CMD規(guī)范和CommonJS規(guī)范是兼容的,相比AMD,它簡(jiǎn)單很多。遵循CMD規(guī)范的模塊,可以在Node.js中運(yùn)行。

define
與AMD規(guī)范不同的是CMD規(guī)范中不使用iddeps參數(shù),只保留factory。其中:
1.factory接收對(duì)象/字符串時(shí),表明模塊的接口就是對(duì)象/字符串。

define({ "foo": "bar" });

define("My name is classicemi.");

define.cmd
其值為一個(gè)空對(duì)象,用于判斷頁(yè)面中是否有CMD模塊加載器。

if (typeof define === "function" && define.cmd) {
  // 使用CMD模塊加載器編寫(xiě)代碼
}

require
此函數(shù)同樣用于獲取模塊接口。如需異步加載模塊,使用require.async方法。

define(function(require, exports, module) {
  require.async("math", function(math) {
    math.add(1, 2);
  });
});

我們可以發(fā)現(xiàn),require(id)的寫(xiě)法和CommonJS一樣是以同步方式加載模塊。要像AMD規(guī)范一樣異步加載模塊則使用define.async方法。

exports
此方法用于模塊對(duì)外提供接口。

define(function(require, exports, module) {
  // 對(duì)外提供foo屬性
  exports.foo = "bar";

  // 對(duì)外提供add方法
  exports.add = function(a, b) {
    return a + b;
  }
});

提供接口的另一個(gè)方法是直接return包含接口鍵值對(duì)的對(duì)象:

define(function(require, exports, module) {
  return {
    foo: "bar",
    add: function(a, b) {
      return a + b;
    }
  }
});

但是注意,不能用exports輸出接口對(duì)象:

define(function(require, exports, module) {
  exports = {
    foo: "bar",
    add: function(a, b) {
      return a + b;
    }
  }
});

這樣寫(xiě)是錯(cuò)誤的!
替代方式是這樣寫(xiě):

define(function(require, exports, module) {
  module.exports = {
    foo: "bar",
    add: function(a, b) {
      return a + b;
    }
  }
});

之前錯(cuò)誤的原因是在factory內(nèi)部,exports實(shí)際上是module.exports的一個(gè)引用,直接給exports賦值是不會(huì)改變module.exports的值的。

在module對(duì)象上,除了有上面提到的exports以外,還有一些別的屬性和方法。
module.id
模塊的標(biāo)識(shí)。

define("math", [], function(require, exports, module) {
  // module.id 的值為 math
});

module.uri
模塊的絕對(duì)路徑,由模塊系統(tǒng)解析得到。

define(function(require, exports, module) {
  console.log(module.uri); // http://xxx.com/path/
});

module.dependencies
值為一個(gè)數(shù)組,返回本模塊的依賴(lài)。

Require.js 和 Sea.js

之前在說(shuō)AMD規(guī)范的時(shí)候提到了Require.js。它是AMD規(guī)范的代表性產(chǎn)品。另一個(gè)Sea.js在前端界也是赫赫有名了,CMD規(guī)范實(shí)際上就是它的產(chǎn)出。它們之間的區(qū)別也很能表現(xiàn)AMD和CMD規(guī)范之間的區(qū)別。

AMD的依賴(lài)需要前置書(shū)寫(xiě)

define(["foo", "bar"], function(foo, bar) {
  foo.add(1, 2);
  bar.subtract(3, 4);
});

CMD的依賴(lài)就近書(shū)寫(xiě)即可,不需要提前聲明:
同步式:

define(function(require, exports, module) {
  var foo = require("foo");
  foo.add(1, 2);
  ...
  var bar = require("bar");
  bar.subtract(3, 4);
});

異步式:

define(function(require, exports, module) {
  ...
  require.async("math", function(math) {
    math.add(1, 2);
  });
  ...
});

雖然AMD也可以用和CMD相似的方法,但不是官方推薦的。

之前在介紹CMD的API時(shí),我們可以發(fā)現(xiàn)其API職責(zé)專(zhuān)一,例如同步加載和異步加載的API都分為requirerequire.async,而AMD的API比較多功能。

總而言之,引用玉伯的總結(jié):
1. Require.js同時(shí)適用于瀏覽器端和服務(wù)器環(huán)境的模塊加載。Sea.js則專(zhuān)注于瀏覽器端的模塊加載實(shí)現(xiàn)。通過(guò)Node擴(kuò)展也可以運(yùn)行于Node環(huán)境中。
2. Require.js -> AMD,Sea.js -> CMD。
3. RequireJS 在嘗試讓第三方類(lèi)庫(kù)修改自身來(lái)支持 RequireJS,目前只有少數(shù)社區(qū)采納。Sea.js 不強(qiáng)推,采用自主封裝的方式來(lái)“海納百川”,目前已有較成熟的封裝策略。
4. Sea.js的調(diào)試工具比較完備,Require.js調(diào)試比較不方便。
5. RequireJS 采取的是在源碼中預(yù)留接口的形式,插件類(lèi)型比較單一。Sea.js 采取的是通用事件機(jī)制,插件類(lèi)型更豐富。

怎么看都像是在自夸啊= =,當(dāng)然它有這個(gè)資格

參考文獻(xiàn)

CommonJS官網(wǎng)

阮一峰博客

AMD Github

CMD Github

Sea.js

Require.js

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/87540.html

相關(guān)文章

  • 淺談JavaScript中的面向?qū)ο?/b>

    摘要:面向?qū)ο竺嫦驅(qū)ο缶幊痰娜Q(chēng)是,簡(jiǎn)稱(chēng),面向?qū)ο缶幊淌怯贸橄蠓绞絼?chuàng)建基于現(xiàn)實(shí)世界模型的一種編程模式。面向?qū)ο缶幊痰娜齻€(gè)主要特征是封裝繼承多態(tài)。 面向?qū)ο?面向?qū)ο缶幊痰娜Q(chēng)是Object Oriented Programming,簡(jiǎn)稱(chēng)OOP,面向?qū)ο缶幊淌怯贸橄蠓绞絼?chuàng)建基于現(xiàn)實(shí)世界模型的一種編程模式。面向?qū)ο缶幊炭梢钥醋鍪鞘褂靡幌盗袑?duì)象相互協(xié)作的軟件設(shè)計(jì),面向?qū)ο蟪绦蛟O(shè)計(jì)的目的是在編程中促...

    Magicer 評(píng)論0 收藏0
  • 如何設(shè)計(jì)大型網(wǎng)站的前端 JavaScript 框架

    摘要:前端單元測(cè)試,推薦淘寶開(kāi)源的工具,簡(jiǎn)單易用,支持眾多測(cè)試框架,也支持調(diào)試。這些也是設(shè)計(jì)前端框架時(shí)需要權(quán)衡的重要方面。最后,其實(shí)大型網(wǎng)站不一定要設(shè)計(jì)自己的前端框架,完全可以選用現(xiàn)有的框架。 有人在知乎上提問(wèn)如何設(shè)計(jì)大型網(wǎng)站的前端 JavaScript 框架,有不少回答,其中得贊較多的兩個(gè)回答如下: 相對(duì)大型的項(xiàng)目在前端 JS 方面有幾個(gè)需要達(dá)成的目標(biāo): 1. 代碼邏輯分層 ...

    Yuanf 評(píng)論0 收藏0
  • Learning PHP —— 設(shè)計(jì)模式 | Chap1:淺談設(shè)計(jì)模式中的OOP

    摘要:而哈士奇區(qū)別于普通狗,又有新的特征逗比,愛(ài)搗亂為了保證類(lèi)之間的松綁定,通常會(huì)繼承抽象類(lèi),而且是淺繼承只有一層子類(lèi)。如果知道所有類(lèi)都會(huì)共享一個(gè)公共的行為實(shí)現(xiàn),就使用抽象類(lèi),并在其中實(shí)現(xiàn)該行為。 為什么使用OOP OOP是一個(gè)模塊化的過(guò)程,目的是為了把復(fù)雜問(wèn)題簡(jiǎn)單化,一個(gè)模塊解決一個(gè)復(fù)雜問(wèn)題的某一個(gè)方面,即一個(gè)類(lèi)應(yīng)當(dāng)只有一個(gè)職責(zé) OOP區(qū)別于順序式編程與過(guò)程式編程,在于: 1.順序編程...

    SunZhaopeng 評(píng)論0 收藏0
  • 淺談JavaScript中的事件循環(huán)機(jī)制

    摘要:事件循環(huán)背景是一門(mén)單線程非阻塞的腳本語(yǔ)言,單線程意味著,代碼在執(zhí)行的任何時(shí)候,都只有一個(gè)主線程來(lái)處理所有的任務(wù)。在意識(shí)到該問(wèn)題之際,新特性中的可以讓成為一門(mén)多線程語(yǔ)言,但實(shí)際開(kāi)發(fā)中使用存在著諸多限制。這個(gè)地方被稱(chēng)為執(zhí)行棧。 事件循環(huán)(Event Loop) 背景 JavaScript是一門(mén)單線程非阻塞的腳本語(yǔ)言,單線程意味著,JavaScript代碼在執(zhí)行的任何時(shí)候,都只有一個(gè)主線程來(lái)...

    Pluser 評(píng)論0 收藏0
  • 2018 淺談前端面試那些事

    摘要:聲明的變量不得改變值,這意味著,一旦聲明變量,就必須立即初始化,不能留到以后賦值。 雖然今年沒(méi)有換工作的打算 但為了跟上時(shí)代的腳步 還是忍不住整理了一份最新前端知識(shí)點(diǎn) 知識(shí)點(diǎn)匯總 1.HTML HTML5新特性,語(yǔ)義化瀏覽器的標(biāo)準(zhǔn)模式和怪異模式xhtml和html的區(qū)別使用data-的好處meta標(biāo)簽canvasHTML廢棄的標(biāo)簽IE6 bug,和一些定位寫(xiě)法css js放置位置和原因...

    LiuRhoRamen 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<