轉(zhuǎn)載地址:http://www.cnblogs.com/dolphinX/p/3485260.html
在開(kāi)發(fā)一個(gè)復(fù)雜的應(yīng)用程序的時(shí)候,我們需要把各個(gè)功能拆分嘁字、封裝到不同的文件夫椭,在需要的時(shí)候引用該文件掸掸。沒(méi)人會(huì)寫一個(gè)幾萬(wàn)行代碼的文件,這樣在可讀性蹭秋、復(fù)用性和維護(hù)性上都很差扰付,幾乎所有的編程語(yǔ)言都有自己的模塊組織方式,比如Java中的包感凤、C#中的程序集等悯周,node.js使用模塊和包來(lái)組織,其機(jī)制實(shí)現(xiàn)參照了CommonJS標(biāo)準(zhǔn)陪竿,雖未完全遵守禽翼,但差距不大屠橄,使用起來(lái)非常簡(jiǎn)單。
什么是模塊
在node.js中模塊與文件是一一對(duì)應(yīng)的闰挡,也就是說(shuō)一個(gè)node.js文件就是一個(gè)模塊锐墙,文件內(nèi)容可能是我們封裝好的一些JavaScript方法、JSON數(shù)據(jù)长酗、編譯過(guò)的C/C++拓展等溪北,在關(guān)于node.js的誤會(huì)提到過(guò)node.js的架構(gòu)
其中http、fs夺脾、net等都是node.js提供的核心模塊之拨,使用C/C++實(shí)現(xiàn),外部用JavaScript封裝咧叭。
創(chuàng)建蚀乔、加載模塊
模塊在node.js中的概念很簡(jiǎn)單,看看如何創(chuàng)建一個(gè)我們自己的模塊供開(kāi)發(fā)復(fù)用菲茬。
在node.js中創(chuàng)建模塊非常簡(jiǎn)單吉挣,一個(gè)文件就是一個(gè)模塊,所以我們創(chuàng)建一個(gè)test.js文件就創(chuàng)建了一個(gè)模塊
test.js
var name='';
function setName(n){
name=n;
}
function printName(){
console.log(name);
}
問(wèn)題是怎么使外部訪問(wèn)這個(gè)module婉弹,我們知道客戶端的JavaScript使用script標(biāo)簽引入JavaScript文件就可以訪問(wèn)其內(nèi)容了睬魂,但這樣帶了的弊端很多,最大的就是作用域相同镀赌,產(chǎn)生沖突問(wèn)題氯哮,以至于前端大師們想出了立即執(zhí)行函數(shù)等方式,利用閉包解決佩脊。node.js使用exports和require對(duì)象來(lái)解決對(duì)外提供接口和引用模塊的問(wèn)題蛙粘。
我們可以把模塊中希望被外界訪問(wèn)的內(nèi)容定義到exports對(duì)象中,對(duì)test.js稍作修改就可以了
test.js
var name='';
function setName(n){
name=n;
}
function printName(){
console.log(name);
}
exports.setName=setName;
exports.printName=printName;
這樣我們?cè)谙嗤窂较聞?chuàng)建index.js威彰,使用require引用一下test.js module
index.js
var test=require('./test');
test.setName('Byron');
test.printName();
exports一個(gè)對(duì)象
有時(shí)候我們希望模塊對(duì)外提供的使一個(gè)對(duì)象出牧,修改一下test.js
test.js
var Student=function(){
var name='';
this.setName=function(n){
name=n;
};
this.printName=function(){
console.log(name) ;
};
};
exports.Student=Student;
這樣我們對(duì)外提供了一個(gè)Student類,在使用的時(shí)候需要這樣
var Student=require('./test').Student;
var student=new Student();
student.setName('Byron');
student.printName();
這樣我們的require語(yǔ)句就可以優(yōu)雅一些了
var Student=function(){
var name='';
this.setName=function(n){
name=n;
};
this.printName=function(){
console.log(name) ;
};
};
module.exports=Student;
很神奇的樣子歇盼,不是說(shuō)好的exports是模塊公開(kāi)的接口嘛舔痕,那么module.exports是什么東西?
module.exports與exports
事實(shí)的情況是醬紫的豹缀,其實(shí)module.exports才是模塊公開(kāi)的接口伯复,每個(gè)模塊都會(huì)自動(dòng)創(chuàng)建一個(gè)module對(duì)象,對(duì)象有一個(gè)modules的屬性邢笙,初始值是個(gè)空對(duì)象{}啸如,module的公開(kāi)接口就是這個(gè)屬性——module.exports。既然如此那和exports對(duì)象有毛線關(guān)系暗摺叮雳!為什么我們也可以通過(guò)exports對(duì)象來(lái)公開(kāi)接口呢想暗?
為了方便,模塊中會(huì)有一個(gè)exports對(duì)象帘不,和module.exports指向同一個(gè)變量说莫,所以我們修改exports對(duì)象的時(shí)候也會(huì)修改module.exports對(duì)象,這樣我們就明白網(wǎng)上盛傳的module.exports對(duì)象不為空的時(shí)候exports對(duì)象就自動(dòng)忽略是怎么回事兒了寞焙,因?yàn)閙odule.exports通過(guò)賦值方式已經(jīng)和exports對(duì)象指向的變量不同了储狭,exports對(duì)象怎么改和module.exports對(duì)象沒(méi)關(guān)系了。
大概就是這么過(guò)程
module.exports=exports={};
......
module.exports=new Object();
exports=xxx;//和new Object沒(méi)有關(guān)系了捣郊,最后返回module.exports辽狈,所以改動(dòng)都無(wú)效了
一次加載
無(wú)論調(diào)用多少次require,對(duì)于同一模塊node.js只會(huì)加載一次呛牲,引用多次獲取的仍是相同的實(shí)例稻艰,看個(gè)例子
test.js
var name='';
function setName(n){
name=n;
}
function printName(){
console.log(name);
}
exports.setName=setName;
exports.printName=printName;
index.js
var test1=require('./test'),
test2=require('./test');
test1.setName('Byron');
test2.printName();
執(zhí)行結(jié)果并不是'',而是輸出了test1設(shè)置的名字侈净,雖然引用兩次,但是獲取的是一個(gè)實(shí)例
require搜索module方式
node.js中模塊有兩種類型:核心模塊和文件模塊僧凤,核心模塊直接使用名稱獲取畜侦,比如最長(zhǎng)用的http模塊
var http=require('http');
在上面例子中我們使用了相對(duì)路徑 './test'來(lái)獲取自定義文件模塊,那么node.js有幾種搜索加載模塊方式呢躯保?
核心模塊優(yōu)先級(jí)最高旋膳,直接使用名字加載,在有命名沖突的時(shí)候首先加載核心模塊
文件模塊只能按照路徑加載(可以省略默認(rèn)的.js拓展名途事,不是的話需要顯示聲明書寫)絕對(duì)路徑
相對(duì)路徑
查找node_modules目錄验懊,我們知道在調(diào)用 npm install <name> 命令的時(shí)候會(huì)在當(dāng)前目錄下創(chuàng)建node_module目錄(如果不存在) 安裝模塊,當(dāng) require 遇到一個(gè)既不是核心模塊,又不是以路徑形式表示的模塊名稱時(shí),會(huì)試圖 在當(dāng)前目錄下的 node_modules 目錄中來(lái)查找是不是有這樣一個(gè)模塊尸变。如果沒(méi)有找到,則會(huì) 在當(dāng)前目錄的上一層中的 node_modules 目錄中繼續(xù)查找,反復(fù)執(zhí)行這一過(guò)程,直到遇到根 目錄為止义图。