J歷史php
咱們都知道,js在剛被建立的時候,只是爲了在網頁上寫一些小腳本而已,好比網頁特效,表單驗證等等,創立者也許沒覺悟到之後的js會發展到如此規模。這是web1.0時代。前端
在web 2.0時代,各類前端庫,前端框架被開發出來,jquery,angular就是表明。此時js的功能也就從寫寫小特效啥的躍遷到了應用開發的級別上。能夠說,js經歷了工具類庫,組件庫,前端框架,前端應用的變遷。java
因而在愈來愈普遍的應用中,js暴露了它先天就缺少的一項功能:模塊。在其它高級語言,都有模塊的定義,java有類,python有import機制,ruby有require,php有require和include。而js此時還單純的用script標籤引入,用命名空間來約束代碼,雜亂無章,因而,commonjs規範便應運而出。node
commonJS的模塊規範python
commonjs規範的出發點就是讓js在任何地方都能運行。它彌補了js此時的幾點缺陷:jquery
沒有模塊概念web
標準庫較少json
沒有標準接口後端
沒有包管理系統瀏覽器
固然,現在commonjs規範已經解決了大部分問題,並且涵蓋了模塊,二進制,Buffer,字符集編碼,I/O流,單元測試,web服務網關接口,包管理,等。
commonjs對模塊的定義很簡單,包含了模塊定義,模塊引用,模塊標識3個部分。
模塊引用
var xx = require('xxx')
關鍵字require來接受模塊標識,引入這個模塊的API到上下文中。
模塊定義
一個例子來解釋:
//add.jsfunction add(a,b){ return a+b;
}// 這樣導出的 add是做爲 exports 的一個方法被導出的exports.add = add;// main.jsvar Add = require('add');console.log(Add.add(1,2));//Add是require引入的模塊名,add是方法名。
node在編譯的時候,會把代碼封裝成以下的樣子
// require 是對 Node.js 實現查找模塊的 Module._load 實例的引用// __finename 和 __dirname 是 Node.js 在查找該模塊後找到的模塊名稱和模塊絕對路徑(function(exports,require,module,__filename,__dirname){ function add (a,b){ return a+b;
}
exports.add = add;
})
爲了將函數直接導出成一個模塊,而不是一個方法,用到了全局變量module,下面就是咱們常見的樣子了:
// add.jsfunction add (a,b){ return a+b ;
}module.exports = add;// main.jsvar add = require('add');console.log(add(1,2));
模塊標識
模塊標識就是require()裏的參數,必須是小駝峯命名的字符串,或者是路徑(./ 或../)。能夠沒有後綴.js
Node的模塊機制
Node並非徹底按照commonjs規範來實現,而是進行了一些取捨,並增長了本身的一些特性。
Node中引入模塊,經歷3個步驟:
路徑分析
文件定位
編譯執行
Node中模塊分爲2種,核心模塊(Node提供的)和文件模塊(用戶本身編寫的)
Node對引入過的模塊會進行緩存,就像前端瀏覽器會緩存靜態腳原本提升性能同樣。require()方法在對同一模塊的二次加載一概採用緩存優先的方式。可是對核心模塊的緩存檢查優先於對文件模塊的緩存檢查。
路徑分析
就是對模塊標識的分析唄。
模塊標識符在Node中分如下幾類:
核心模塊,如http,path
./ ../相對路徑
/ 絕對路徑
非路徑形式的文件模塊。
文件分析
若是模塊標識符沒有後綴,默認補上後綴從.js,.json,.node來次序查找。
模塊編譯
js編譯上面已經提到了。Node對JS文件進行了包裝,在頭部添加了(function (exports, require, module, __filename, __dirname) {...})。這樣每一個模塊都進行了做用域隔離。包裝以後經過vm原生模塊的runInThisContext()方法執行(相似eval,只是具備明確上下文,不污染全局)返回一個function。而後將上述參數傳給這個function執行。
json編譯更簡單,Node直接用JSON.parse()方法編譯json內容,獲得的對象賦給exports。
包和NPM
commonjs的包規範包含2個組成部分,包結構和包描述文件
包描述文件:package.json
包結構:
package.json 包描述文件
bin: 存放可執行二進制文件的目錄
lib 存放js代碼的目錄
doc. 存放文檔的目錄
test: 存放單元測試用例的代碼
NPM的用法就很少說了。
先後端公用模塊
自從Node出來之後,js也能夠運用在後端。可是先後端的JS扮演的角色不一樣,瀏覽器端的js須要經歷從同一個服務器分發到多個客戶端執行。服務端的js則是相同的代碼屢次執行。前者的瓶頸在於帶寬。後者的瓶頸在於CUP和內存。前者須要經過網絡加載。後者從磁盤中加載。
由於Node基於commonjs規範來同步的加載模塊的。前端若用同步方式來加載模塊,在用戶體驗上會形成很大的問題。UI在初始化的時候須要等待很長時間來加載js腳本。因此提出了異步模塊定義AMD和CMD。這在個人另外一篇博客中有提到。就很少說了。
爲了寫個能兼容先後端的模塊規範,類庫的開發者要把代碼包裝在一個 閉包裏。這樣就能兼容Node,AMD,CMD和常見的瀏覽器。