js實現編寫後端程序的不足之處前端
1. 沒有模塊系統(js一個先天不足就是模塊功能)node
2. ECMAScript僅僅定義了js的核心庫,可是對於文件系統、IO系統等卻沒有標準的API。HTML5雖然在一直致力於推動標準化,可是這些標準耶都是前端的。c++
3. 沒有標準接口,沒有定義過服務器或者數據庫的接口。git
4. 缺少包管理系統,沒有自動安裝和管理依賴的能力。github
而CommonJS的出現正好彌補了沒有標準的這一個缺點。
CommonJS
規範了 模塊
、二進制
、Buffer
、二進制
、I/0
,進程環境
、文件系統
、web服務器網管接口
、包管理等
。web
Node的出現離不開CommonJS規範的影響,而CommonJS能以一種獨尋常的姿態出如今各大公司的代碼中,離不開Node優異的表現。數據庫
1. 模塊引入npm
var http = require("http");
2. 模塊導出json
// math.js exports.add = function(){ ... }
// program.js Var math = require('math.js'); exports.increament = function(val){ return math.add(val); }
3. 模塊標識後端
模塊標識就是require()
的括號中的參數,必須是小駝峯結構,能夠是相對路徑也能夠是絕對路徑,還能夠沒有後綴名。
node 的模塊實現其實是借鑑了CommonJS的部分,並非所有照搬,並且也增長了一些本身須要的東西進去。
1. 模塊引入
node中模塊引入須要通過:
1. 路徑分析 2. 文件定位 3. 編譯執行
2. 模塊分類
1. 核心模塊 Node提供的 2. 文件模塊 用戶編寫的
3. 核心模塊
核心模塊部分在Node源碼加載中就編譯完畢了,編譯成了二進制執行文件,在Node進程啓動後,部分核心模塊就直接加載進內存中,因此能夠不用`文件定位`和`編譯`,所以部分核心模塊的加載時最快的。
4. 文件模塊
文件模塊則是在運行時動態加載,要通過完整的路徑分析、文件定位、編譯執行,一次通常比較慢。
5. 緩衝加載
node對二次加載的核心模塊,一概採用的時緩衝優先的原則。
6. 路徑分析和文件定位
模塊標識符分析
核心模塊的加載僅次於緩衝加載
首先將其轉換爲真實的路徑,而後以真實路徑做爲索引,將其編譯後放進緩衝中,等待調用。 因爲是經過確切的文件地址找到的,因此須要花費必定的時間,
這是非核心模塊,也不是路徑形式的標識,它是一種特殊的文件模塊,多是一個文件或者是一個包。這種查找是最費時間的。首先須要知道一下/模塊路徑/的概念/ > 例子 好比你要加載一個包,這個包放在了node_modules文件夾下,你要引入的話能夠不以路徑的形式寫,能夠是隻寫名稱。(也就是引入一個本身npm的包) console.log(module.paths); // 會獲得下面的數組 [ 'D:\\myweb\\node\\node\\module\\node_modules', 'D:\\myweb\\node\\node\\node_modules', 'D:\\myweb\\node\\node_modules', 'D:\\myweb\\node_modules', 'D:\\node_modules' ] 這就是模塊路徑了,查找機制是: 首先在當前路徑下找是否有node_modules文件夾下的該包,若是沒有就查找上一層目錄,依次類推,直到根目錄下的node_modules,若是依舊沒有找到,那麼就報錯了。 // 顯然這種狀況就是致使它速度較慢的主要緣由了,(查找的路徑越深就越慢),可是咱們能夠經過一些小技巧來經可能的減小這種狀況哦~~
文件定位
文件定位中還須要注意的包括有文件擴展名
、目錄
、包的處理
。
node 的模塊引入的時候是能夠不寫擴展名的,node會按照js
json
node
的順序來分析。依次嘗試。因爲嘗試使用的是node中fs模塊的同步文件查找,所以可能會致使阻塞狀況發生,所以這裏咱們須要注意兩個小技巧了:
小技巧:
1. json
node
文件最好加擴展名
2. 同步配合緩衝能夠環節Node單線程阻塞調用的缺陷
另外,若是沒有找到對應的文件,確實找到了一個目錄,那麼將會將其看成是一個包來處理了。
如何在這個包下找到咱們須要引入的入口文件對呢?
1. 首先找是否含有package.json,若是有,則分析它的main屬性,找到main屬性對應的那個文件。
2. 若是沒有package.json或者是main解析失敗了,那麼就找文件名爲index的文件,依次從index.js
index.json
index.node
查找。
3. 若是在該目錄下依舊沒有找到,那麼就查找寫一個匹配的目錄,若是仍然沒有找到,那麼就報錯了。
6. 模塊編譯
node中每個模塊都是一個對象,當定位到一個文件的時候,node就會將其包裝成一個module
對象,而後根據不一樣的文件名,其載入方法也不一樣的。
7. module.exports和exports的區別和聯繫
exports = module.exports = {};
的區別就和 var a = {}; b = a;
的區別同樣.
首先要明確的一點,module
是一個對象 {Object}。
當你新建一個文件,好比mo.js
,文件內容以下:
console.log(module);
而後在CMD裏執行這個文件node mo.js,就能看到module實際上是一個Module實例,你能夠這麼理解,NodeJS中定義了一個Module類,這個類中有不少屬性和方法,exports是其中的一個屬性:
function Module { id : 'blabla', exports : {}, blabla... }
當每個文件被執行或者時require的時候,node就會建立一個module實例。var module = new Module();
console.log(module); //你會看到Module中的exports爲空對象{} module.exports = { print : function(){console.log(12345)} } console.log(module); //你會看到Module中的exports對象已經有了print()方法
module.exports 其實就是module實例中的module添加方法或者屬性。
console.log(module); //你會看到Module中的exports爲空對象{} console.log(exports); //你會看到Module中的exports爲空對象{} module.exports = { print : function(){console.log(12345)} } console.log(module); //你會看到Module中的exports對象有了print()方法 exports.name = '小白妹妹'; console.log(module); //你會看到Module中的exports對象不只有了print()方法,還有了name屬性
不難看出exports其實就是module.exports的一個引用。
// 你能夠這麼理解 var module = new Module(); var exports = module.exports;
當你require的時候,返回的就是module.exports的內容。
// 經常使用場景分析 module.exports.name = '小白妹妹'; exports.age = 10; module.exports.print = function(){console.log(12345)}; //若是隻是添加屬性和方法,二者能夠混用。 // 也能夠 module.exports = { name = '小白妹妹'; }; exports.age = 10; module.exports.print = function(){console.log(12345)}; // 【X】可是不能夠 module.exports = { name = '小白妹妹'; }; exports = {age:10}; // exports如今是{age:10}這個對象的引用,再也不是module.exports的引用了 console.log(module); //你會看到Module的exports中只有name屬性!!! // 【X】 exports.age = 10; console.log(module); //你會看到Module的exports中多了age屬性 module.exports = { name = '小白妹妹'; }; // 直接改變了module.exports的引用,以前的屬性值也被覆蓋掉了。 console.log(module); //你會看到Module的exports中仍是隻有name屬性!!!
總結
- 改變exports的指向後所添加的exports.xxx都是無效的。由於require返回的只會是module.exports
- 不能在使用了exports.xxx以後,改變module.exports的指向。由於exports.xxx添加的屬性和方法並不存在於module.exports所指向的新對象中。
- 對於要導出的屬性,能夠簡單直接掛到exports對象上
- 對於類,爲了直接使導出的內容做爲類的構造器可讓調用者使用new操做符建立實例對象,應該把構造函數掛到module.exports對象上,不要和導出屬性值混在一塊兒