在一個(gè)模塊文件中定義的本地(即非函數(shù)內(nèi)部定義的)變量、函數(shù)或者對(duì)象都只在該模塊內(nèi)部有效廊勃,當(dāng)你需要從模塊外部引用這些變量懈贺、函數(shù)或者對(duì)象時(shí),需要在該模塊內(nèi)部使用exports
或者module.exports
對(duì)象坡垫。
開發(fā)者可以在“全局”環(huán)境下任意使用var申明變量(不用寫到閉包里了)梭灿,通過exports
暴露接口給調(diào)用者。
是否糾結(jié)于什么時(shí)候該用exports
什么時(shí)候該用module.exports
呢?先簡(jiǎn)要陳述一下兩者的區(qū)別冰悠,分別給出兩個(gè)例子后堡妒,在具體說(shuō)一下使用的場(chǎng)景睡扬。
對(duì)于要導(dǎo)出的屬性舷胜,可以簡(jiǎn)單直接掛到exports對(duì)象上
對(duì)于類,為了直接使導(dǎo)出的內(nèi)容作為類的構(gòu)造器可以讓調(diào)用者使用
new
操作符創(chuàng)建實(shí)例對(duì)象乘寒,應(yīng)該把構(gòu)造函數(shù)掛到module.exports
對(duì)象上的诵,不要和導(dǎo)出屬性值混在一起
exports
測(cè)試一下
創(chuàng)建一個(gè)文件作為我們自定義的格式化工具類万栅,起名叫做format.js
。在函數(shù)內(nèi)我們定一個(gè)格式化金錢的函數(shù)formatMoney
并使用exports
對(duì)外導(dǎo)出西疤,再定義一個(gè)函數(shù)烦粒,不導(dǎo)出
function formatMoney(s, n) {
if (isNaN(s)) {
return '--';
}
var negative = false;
if (s < 0) {
negative = true;
s = Math.abs(s);
}
try {
n = n >= 0 && n <= 20 ? n : 2;
s = parseFloat((s + "").replace(/[^\d\.-]/g, "")).toFixed(n) + "";
var l = s.split(".")[0].split("").reverse(), r = s.split(".")[1];
t = "";
for (i = 0; i < l.length; i++) {
t += l[i] + ((i + 1) % 3 === 0 && (i + 1) !== l.length ? "," : "");
}
r = r ? '.' + r : '';
var result = t.split("").reverse().join("") + r;
if (negative) {
return '-' + result;
} else {
return result;
}
}
catch (e) {
return '';
}
}
function funcOnlyMe() {
console.log('This function can not be used by other file')
}
exports.formatMoney = formatMoney;
在同層的test.js文件中引用模塊文件,調(diào)用模塊暴漏給外部的方法和私有方法
var format = require('./format');
var moneyOutput1 = format.formatMoney('1982400');
console.log(moneyOutput1);
//1,982,400.00
format.funcOnlyMe();
// format.funcOnlyMe()
// ^
// TypeError: format.funcOnlyMe is not a function
// at Object.<anonymous> (/Users/beifeng/Desktop/test_node/test.js:6:8)
// at Module._compile (module.js:398:26)
// at Object.Module._extensions..js (module.js:405:10)
// at Module.load (module.js:344:32)
// at Function.Module._load (module.js:301:12)
// at Function.Module.runMain (module.js:430:10)
// at startup (node.js:141:18)
// at node.js:1003:3
module.exports
需要將模塊定義為一個(gè)類的時(shí)候代赁,需要使用module.exports
的書寫方法扰她。
將模塊封裝成類的演示1
// module_test.js
var _name;
var name = '';
// 模塊對(duì)象的構(gòu)造函數(shù)
var foo = function(name) {
_name = name;
}
//獲取私有變量 _name的變量值
foo.prototype.GetName = function() {
return _name;
}
//設(shè)置私有變量 _name的變量值
foo.prototype.SetName = function(name) {
_name = name;
}
foo.prototype.name = name;
module.exports = foo;
//main.js
var foo = require('./module_test');
var myFoo = new foo('feng');
console.log(myFoo.GetName());//feng
myFoo.SetName('liang');
console.log(myFoo.GetName());//liang
封裝一個(gè)自定義的格式化價(jià)格的工具
module.exports = {
formatMoney: function (s, n) {
if (isNaN(s)) {
return '--';
}
var negative = false;
if (s < 0) {
negative = true;
s = Math.abs(s);
}
try {
n = n >= 0 && n <= 20 ? n : 2;
s = parseFloat((s + "").replace(/[^\d\.-]/g, "")).toFixed(n) + "";
var l = s.split(".")[0].split("").reverse(), r = s.split(".")[1];
t = "";
for (i = 0; i < l.length; i++) {
t += l[i] + ((i + 1) % 3 === 0 && (i + 1) !== l.length ? "," : "");
}
r = r ? '.' + r : '';
var result = t.split("").reverse().join("") + r;
if (negative) {
return '-' + result;
} else {
return result;
}
}
catch (e) {
return '';
}
},
formatWan: function (s, n) {
s = s / 10000;
n = n || 0;
return this.formatMoney(s, n);
},
formatYi: function (s, n) {
s = s / 100000000;
n = n || 0;
return this.formatMoney(s, n);
},
moneyUnit: function (money) {
if (parseFloat(money) < 1) {
return money * 100 + '分'
} else if (parseInt(money) < 10000) {
return money + '元'
} else if (parseInt(money) < 100000000) {
return this.formatWan(money, 2) + '萬(wàn)元'
} else {
return this.formatYi(money, 2) + '億元'
}
}
}
exports 和 module.exports區(qū)別
Node.js在模塊編譯的過程中會(huì)對(duì)模塊進(jìn)行包裝,最終會(huì)返回類似下面的代碼:
(function (exports, require, module, __filename, __dirname) {
// module code...
});
其中芭碍,module就是這個(gè)模塊本身徒役,require是對(duì)Node.js實(shí)現(xiàn)查找模塊的模塊Module._load實(shí)例的引用,__filename和__dirname是Node.js在查找該模塊后找到的模塊名稱和模塊絕對(duì)路徑窖壕,這就是官方API里頭這兩個(gè)全局變量的來(lái)歷忧勿。
關(guān)于module.exports與exorts的區(qū)別,了解了下面幾點(diǎn)之后應(yīng)該就完全明白:
模塊內(nèi)部大概是這樣:
exports = module.exports = {};
exports是module.exports的一個(gè)引用瞻讽。
require引用模塊后鸳吸,返回給調(diào)用者的是module.exports而不是exports
exports.xxx
相當(dāng)于在導(dǎo)出對(duì)象上掛屬性,該屬性對(duì)調(diào)用模塊直接可見exports =
相當(dāng)于給exports對(duì)象重新賦值速勇,調(diào)用模塊不能訪問exports對(duì)象及其屬性如果此模塊是一個(gè)類晌砾,就應(yīng)該直接賦值
module.exports
,這樣調(diào)用者就是一個(gè)類構(gòu)造器烦磁,可以直接new實(shí)例
也就是說(shuō)养匈,首先以下幾種導(dǎo)出方式是一致的
exports.str = 'a';
exports.func = function () {};
module.exports = {
str : 'a',
func : function(){}
}
exports = module.exports = {
str : 'a',
func : function(){}
}
引用這個(gè)模塊的文件,看到的都是相同的內(nèi)容
var foo = require('./module_test');
console.log(foo)
//{ str: 'a', func: [Function] }
但是如果修改重寫exports
屬性的話个初,情況就大不一樣了乖寒。在下面文件中,我們先給exports
對(duì)象設(shè)置兩個(gè)屬性院溺,然后重寫exports
屬性楣嘁,最后再給exports
對(duì)象設(shè)置兩個(gè)新屬性
exports.str1 = 'before overwrite exports'
exports.func1 = function () {
console.log(this.str1);//此處的this代表當(dāng)前導(dǎo)出的exports對(duì)象
};
exports = {
str2:'inner exports',
func2:function(){}
}
exports.str3 = 'after overwrite';
exports.func3 = function () {};
引用這個(gè)模塊的文件
var foo = require('./module_test');
console.log(foo)
//{ str1: 'before overwrite exports', func1: [Function] }
發(fā)現(xiàn)一個(gè)有趣的現(xiàn)象:重寫exports對(duì)象的一刻起,exports
對(duì)象就失去了繼續(xù)對(duì)外導(dǎo)出屬性的能力珍逸,外部文件只能訪問重寫之前的exports
對(duì)象屬性逐虚。
什么時(shí)候使用exprots 什么時(shí)候使用module.exports
有一點(diǎn)需要重申:exports
只是module.exports
的輔助方法。你的模塊最終返回module.exports
給調(diào)用者谆膳,而不是exports
叭爱。exports
所做的事情是收集屬性,如果module.exports
當(dāng)前沒有任何屬性的話漱病,exports
會(huì)把這些屬性賦予module.exports
买雾。如果module.exports
已經(jīng)存在一些屬性的話把曼,那么exports
中所用的東西都會(huì)被忽略。
module.exports = 'hello world';
exports.name = function() {
console.log('sorry , i am fail');
};
引用這個(gè)模塊的文件
var foo = require('./module_test.js');
foo.name(); // TypeError: Object hello world has no method 'name'
foo
模塊完全忽略了exports.name
漓穿,然后返回了一個(gè)字符串’hello world ’嗤军。通過上面的例子,你可能認(rèn)識(shí)到你的模塊不一定非得是模塊實(shí)例晃危。你的模塊可以是任何合法的JavaScript對(duì)象 - boolean叙赚,number,date僚饭,JSON震叮, string,function鳍鸵,array
和其他苇瓣。你的模塊可以是任何你賦予module.exports
的值。如果你沒有明確的給module.exports
設(shè)置任何值权纤,那么exports
中的屬性會(huì)被賦給module.exports
中钓简,然后并返回一個(gè)包含這些屬性的對(duì)象。
如果你的模塊是一個(gè)類
module.exports = function(name, age) {
this.name = name;
this.age = age;
this.about = function() {
console.log(this.name +' is '+ this.age +' years old');
};
};
這樣調(diào)用它
var foo = require('./module_test.js');
var people = new foo('feng', 30);
people.about(); // feng is 26 years old
在下面的情況下汹想,你的模塊是一個(gè)數(shù)組:
module.exports = ['a', 'b', 'c', 'd', 'e'];
然后這樣調(diào)用
var foo = require('./module_test.js');
console.log('which one: ' + rocker[2]); //c
所以:如果你想要你的模塊成為一個(gè)特別的對(duì)象類型外邓,那么使用module.exports
;如果你希望你的模塊成為一個(gè)傳統(tǒng)的模塊實(shí)例古掏,使用exports
,因?yàn)檫@貨基本上只能返回一個(gè)包含若干屬性的對(duì)象损话。
話雖如此,exports
還是推薦的對(duì)象槽唾,除非你想把你模塊的對(duì)象類型從傳統(tǒng)的模塊實(shí)例修改為其他的類型的返回值丧枪。
希望對(duì)您有用~