NodeJS學習筆記: require, exports 和 module.exports 的初印象

前言

本人不是技術專家,該筆記只是從使用語言進行開發的層面上記錄一些體會,不包含也不想嘗試從源碼或者更深的層次去討論語言自己的優劣。文章內容是筆者的我的感悟,既不保證正確性,也不保證別人能看懂。html

這是該筆記的第一篇,雖然不肯定之後會不會有第二篇。node

引子

最近不在項目裏,因此打算趁此機會了解一下MEANJS架構,磕磕絆絆的配置好環境以後就嘗試開始熟悉它了。學習的捷徑就是模仿,這是個人我的經驗,可是模仿以前總要先看明白示例代碼吧,結果悲劇的發現,我被隨處可見的 require, exports 和 module.exports 搞暈了,又是一通各類搜索前人經驗的過程,而後終於有點明白了,特此記錄下來。json

正文

require方法按照必定的規則(規則見附錄)去尋找傳入的參數對應的js文件,這個文件必須爲exports或者module.exports賦值,這個值會被做爲require方法的返回值傳遞給調用者。架構

示例代碼 main.js學習

'use strict';
var me = require('./module.js');
console.log(me); //Hello require!

示例代碼 module.jsui

'use strict';
exports = module.exports = 'Hello require!';
// 也能夠寫成 exports = 'Hello require!';
// 也能夠寫成 module.exports = 'Hello require!';
// 可是一般仍是使用 exports = module.exports = something 這種格式

exports和module.exports有細微的區別,能夠參考這篇文章《nodejs中exports與module.exports的區別》,這裏很少說。this

從另外一個角度來講,require模擬了其餘語言的面向對象開發機制,咱們能夠在一個文件中聲明對象,而後用require來引用,只要記得在聲明對象的結尾寫上prototype

exports = module.exports = yourClass;

就能夠了。下面是一個擴展的示例。code

//demo.js
'use strict';
var mT = require('./test.js');
var mT1 = new mT(); //此時a = 5
mT1.e(); //此時a = 6
var mT2 = new mT();
console.log(mT1.d()); //6
console.log(mT1.d()); //7
console.log(mT1.d()); //8
console.log(mT2.d()); //7
console.log(mT2.d()); //8
//test.js
'use strict';
var a = 5;
var b = function(){
    this.c = a;
    this.d = function() {
        this.c ++;
        return this.c;
    };
    this.e = function() {
        a++;
    };
    return this;
};
exports = module.exports = b;

以上test.js中的b是對象的聲明,a能夠理解爲一個靜態變量,所以mT1修改了a的值以後致使mT2中c的初始值是6而再也不是5,而因爲變量做用域的關係,mT1中的c的值的變化並不會對mT2中的c的值形成影響。理解這一點以後,咱們就能夠自如的聲明出普通對象、單件對象,以及對訪問域進行區別(公開的或者受保護的),一樣的也能夠用來模擬抽象方法,如htm

var o = function() {};
o.prototype.getName = function() {
    throw "請在子類中實現該方法!";
};
var oc = function() {};
oc.prototype = new o();
oc.prototype.getName = function() {
    console.log('yes');
};
var t1 = new oc();
t1.getName(); // yes
var t2 = new o();
t.getName(); // exception

雖然這個作法看起來很傻,可是確實能夠幫到咱們。

結語

如今,我終於能比較流暢的去閱讀示例的源碼了,相信這是一個好的開始。閱讀源碼實際上是一件頗有趣的事情,你能夠先去推測做者的思路,而後看看本身是否是猜對了,若是猜對了,那麼我能夠節約不少時間(既然思路同樣,那麼具體的實現方式其實不是很重要),若是猜錯了,想一下他爲何這麼作,補益自身。

附錄

  1. require方法尋址規則

require(X) from module at path Y
    1. If X is a core module,
       a. return the core module
       b. STOP
    2. If X begins with './' or '/' or '../'
       a. LOAD_AS_FILE(Y + X)
       b. LOAD_AS_DIRECTORY(Y + X)
    3. LOAD_NODE_MODULES(X, dirname(Y))
    4. THROW "not found"
    
    LOAD_AS_FILE(X)
    1. If X is a file, load X as JavaScript text.  STOP
    2. If X.js is a file, load X.js as JavaScript text.  STOP
    3. If X.json is a file, parse X.json to a JavaScript Object.  STOP
    4. If X.node is a file, load X.node as binary addon.  STOP
    
    LOAD_AS_DIRECTORY(X)
    1. If X/package.json is a file,
       a. Parse X/package.json, and look for "main" field.
       b. let M = X + (json main field)
       c. LOAD_AS_FILE(M)
    2. If X/index.js is a file, load X/index.js as JavaScript text.  STOP
    3. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP
    4. If X/index.node is a file, load X/index.node as binary addon.  STOP
    
    LOAD_NODE_MODULES(X, START)
    1. let DIRS=NODE_MODULES_PATHS(START)
    2. for each DIR in DIRS:
       a. LOAD_AS_FILE(DIR/X)
       b. LOAD_AS_DIRECTORY(DIR/X)
    
    NODE_MODULES_PATHS(START)
    1. let PARTS = path split(START)
    2. let I = count of PARTS - 1
    3. let DIRS = []
    4. while I >= 0,
       a. if PARTS[I] = "node_modules" CONTINUE
       c. DIR = path join(PARTS[0 .. I] + "node_modules")
       b. DIRS = DIRS + DIR
       c. let I = I - 1
    5. return DIRS
相關文章
相關標籤/搜索