但願編寫程序能像玩積木同樣,首先規劃要產出怎樣的做品,而後在積木堆中挑選合適的積木塊,最後一組合就完工了。html
因而JavaScript須要相似這樣模塊化,每一個模塊都隱藏內部細節而且對外暴露接口,再處理好模塊之間的依賴關係,就能夠達到玩積木的效果了。git
雖然現有不少模塊化框架/工具,但對於新手來講,爲何不本身擼一個簡單的模塊化工具呢?github
但願經過這個工具把本身以爲好用的代碼以模塊的方式組織起來,漸漸造成本身的JS庫,以後能夠勇敢地和HR說,本身的小項目用的是本身小JS庫,^_^。我以爲,在這個封裝的過程當中,新手能學習到不少東西。gulp
新手嘛,多造輪子老是有好處的,=_=。segmentfault
如下是《你所不知道的JavaScript(上卷)》中對於閉包的說明。數組
當函數能夠記住並訪問所在的詞法做用域,即便函數是在當前詞法做用域以外執行,這時就產生了閉包。閉包
其實,無論怎樣,閉包正如其字面意思同樣,既能提供一個相對封閉的空間,也能向外界暴露必要的接口。這不就正符合咱們模塊化的需求嗎?app
在此,建議參考這篇文章,以增強您對閉包的理解:《假如技術HR問您JavaScript的「閉包」,嘿嘿嘿,舉這個例子就夠了》。框架
var test = (function test(){ function run(){ console.log("run test"); } return { run: run }; })(); test.run();
上面的代碼有個叫test的函數做爲模塊建立器,每次調用它均可以建立一個新的模塊。這裏使用當即執行函數,當即建立了一個test模塊。參考閉包的概念,外部能夠調用test模塊中的run函數,同時test模塊又有本身獨立的做用域。能達到一個積木塊(模塊)的要求。模塊化
簡單的模塊這樣寫沒有問題,可是模塊間的依賴問題沒有解決。
模塊之間必然會存在依賴關係,而模塊管理工具須要可以很好地管理模塊間的依賴。下面咱們模仿實現了AMD規範的工具requirejs,主要是模仿其define,get的API風格,本身寫一個簡單的版本。
//模塊管理工具,MyModules var MyModules = (function Manager() { var modules = {}; function define(name, deps, impl) { for (var i=0; i<deps.length; i++){ //將依賴的名字替換成已經註冊了的模塊 deps[i] = modules[deps[i]]; } //將依賴數組展開成參數傳入模塊的構建函數,生成新模塊 modules[name] = impl.apply(impl, deps); } function get(name){ return modules[name]; } return { define: define, get: get } })(); //定義一個模塊,data MyModules.define("data",[],function(){ var name = "miku"; function getName(){ return name; } return { getName:getName } }); //定義一個模塊,app //該模塊依賴data模塊 MyModules.define("app", ["data"], function(data){ function run(){ console.log(data.getName()); } return { run:run } }); //取出app模塊 var app = MyModules.get("app"); //調用app模塊的run方法 app.run();
能夠看出,利用MyModules能夠很方便地定義使用模塊,管理模塊依賴。可是還存在一個問題,MyModules對於模塊定義的順序有要求。以上面的例子來講,就是app模塊依賴data模塊,那data模塊必須在app模塊以前被定義。這個限制讓咱們實際使用中不是很方便。接下來咱們將改進它。
咱們須要讓模塊管理工具不須要限制模塊的定義順序,這裏個人作法是,使用一個rebuilds數組來保存未成功構建的模塊。每次有新模塊構建成功的時候就會從新嘗試去構建整個rebuilds數組中的模塊。具體看下面的代碼。
window.mm_modules = (function Manager() { var debug = false; var modules = {}; var rebuilds = []; function copyArray (array){ var tempArray = []; for(var i=0; i<array.length; i++){ tempArray.push(array[i]); } return tempArray; } function define(name, deps, impl) { //判斷依賴構建是否成功 var depsDone = true; //拷貝一份當前想要構建的模塊,當構建失敗時使用 var rebuildCopy = { name : name, deps : copyArray(deps), impl : impl }; //循環依賴數組,構建依賴 for (var i=0; i<deps.length; i++){ //將依賴的名字替換成已經註冊了的模塊 deps[i] = modules[deps[i]]; //有依賴的模塊未定義,因此這個模塊構建失敗 if(!deps[i]){ depsDone = false; break; } } //若是依賴構建成功,即模塊構建成功 if(depsDone){ //將依賴數組展開成參數傳入模塊的構建函數,生成新模塊 modules[name] = impl.apply(impl, deps); //從rebuilds數組中移除 if(rebuilds[name]){ delete rebuilds[name]; } //循環rebuilds數組,嘗試重新構建以前構建失敗的模塊 for (key in rebuilds){ var rebuild = rebuilds[key]; if(rebuild){ //遞歸調用 define(rebuild.name, rebuild.deps, rebuild.impl); } } } //模塊構建失敗,存入rebuilds數組中,等待下一次從新構建 else{ rebuilds[name] = rebuildCopy; } if(debug){ console.log("[mm_modules debug]need rebuild modules:", rebuilds); } } function get(name){ return modules[name]; } return { define: define, get: get } })();
改進後的模塊管理工具,可以自動地處理模塊依賴,而不須要限制定義順序了。
那,能不能更進一步呢?試着想一下,咱們平常會怎麼使用?單文件單模塊,而後把這些文件放在不一樣文件夾裏組織好。因而,我就想到使用gulp這樣的工具輔助咱們。
請參考下面的目錄結構。
├── dist │ ├── index.html │ └── js │ └── mm-modules-build.js ├── gulpfile.js ├── mm-modules │ ├── queryObject.js │ ├── request.js │ ├── template.js │ ├── test.js │ └── util.js ├── mm-modules.js
能夠在mm-modules下隨意地定義模塊,如util模塊內有各類工具函數,template模塊則包含了artTemplate模版引擎。以後利用gulp將mm-modules.js(模塊管理工具)與mm-modules下全部的模塊文件打包成mm-modules-build.js。項目中只要引入mm-modules-build.js便可。
到此,咱們本身構建了一個很實用的JavaScript模塊化工具,項目的源碼在這裏:https://github.com/MIKUScallion/mm-modules,喜歡的話,給個✨。
再回顧一下前言的話。
但願經過這個工具把本身以爲好用的代碼以模塊的方式組織起來,漸漸造成本身的JS庫,以後能夠勇敢地和HR說,本身的小項目用的是本身小JS庫,^_^。我以爲,在這個封裝的過程當中,新手能學習到不少東西。
新手嘛,多造輪子老是有好處的,=_=。
《你所不知道的JavaScript(上卷)》