exprots && module.exports

在一個(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ì)您有用~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市庞萍,隨后出現(xiàn)的幾起案子拧烦,更是在濱河造成了極大的恐慌,老刑警劉巖钝计,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件恋博,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡私恬,警方通過查閱死者的電腦和手機(jī)债沮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)本鸣,“玉大人疫衩,你說(shuō)我怎么就攤上這事∪俚拢” “怎么了闷煤?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵童芹,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我鲤拿,道長(zhǎng)辐脖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任皆愉,我火速辦了婚禮,結(jié)果婚禮上艇抠,老公的妹妹穿的比我還像新娘幕庐。我一直安慰自己,他們只是感情好家淤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布异剥。 她就那樣靜靜地躺著,像睡著了一般絮重。 火紅的嫁衣襯著肌膚如雪冤寿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天青伤,我揣著相機(jī)與錄音督怜,去河邊找鬼。 笑死狠角,一個(gè)胖子當(dāng)著我的面吹牛号杠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播丰歌,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼姨蟋,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了立帖?” 一聲冷哼從身側(cè)響起眼溶,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎晓勇,沒想到半個(gè)月后堂飞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宵蕉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年酝静,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片羡玛。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡别智,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出稼稿,到底是詐尸還是另有隱情薄榛,我是刑警寧澤讳窟,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站敞恋,受9級(jí)特大地震影響丽啡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜硬猫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一补箍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧啸蜜,春花似錦坑雅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至蜂林,卻和暖如春遥诉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背矮锈。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留睁蕾,地道東北人愕难。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像猜丹,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子将塑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容