Alex Liang

NodeJS - 初探Module (1)

在學習NodeJS的過程中,module是結構化程式基本的組塊。
我們能藉由module將各function block分開來,並且在主程式引用其method或property
然而,要使用module的method或property有許多方式,本文整理常用的幾種並說明特性。

Module基本使用方式

假設我們有二個js程式:app.js和greet.js,想要讓app.js使用greet.js的method,則需要使用module

greet.js
1
2
3
4
5
var greet = function() {
console.log('Hello');
}

module.exports = greet;
app.js
1
2
var greet = require('./greet');
greet();
1
> Hello

要在app.js使用greet.js的greet方法,得先require greet.js
require預設是引入javascript檔案,所以副檔名可省略。
而greet.js中,將greet這個函式變數設為module.exports的參考
module.exports其實是個object,供其它js檔案使用其method

上述的寫法可以簡化為:

greet.js
1
2
3
module.exports = function() {
console.log('Hello');
}

app.js
1
2
var greet = require('./greet');
greet();

也是一樣的結果

使用Function Expression

greet2.js
1
2
3
module.exports.greet = function() {
console.log('Hello World');
}
app.js
1
2
var greet2 = require('./greet2').greet;
greet2();
1
> Hello World

利用function expression讓module.exports多一個method

使用Function Constructor

greet3.js
1
2
3
4
5
6
7
8
function Greetr() {
this.greeting = 'Hello world!!';
this.greet = function() {
console.log(this.greeting);
}
}

module.exports = new Greetr();
app.js
1
2
var greet3 = require('./greet3');
greet3.greet();
1
> Hello world!!

使用function constructor建立object,並利用this存取function
module.exports則指向新的function constructor

此例有個需要注意的地方,假如我在app.js改變greeting的值,會發生什麼事??

app.js
1
2
3
4
5
var greet3 = require('./greet3');
greet3.greeting = 'Changed';

var greet3b = require('./greet3');
greet3b.greet();

改變greeting的值以後,我們再require greet3並且呼叫method,則結果如下:
1
2
> Hello world!!
> Changed

這裡帶出一個module重要的特性:NodeJs的核心會cache同一個module的參考,所以當我們宣告greet3b為greet3的object時,其實底層是回傳上一個greet3的參考,而此時greeting己被改變。

所以在不同的js檔案使用同一個module時,需注意其實回傳的都是同一份copy

Function Constructor變形

greet4.js
1
2
3
4
5
6
7
8
function Greetr() {
this.greeting = 'Hello world!!!';
this.greet = function() {
console.log(this.greeting);
}
}

module.exports = Greetr;
app.js
1
2
3
var greet4 = require('./greet4');
var greetr = new greet4();
greetr.greet();

這種方式把建構子放在app.js,所以不會有二個require指向同一份copy

Revealing Module Pattern

greet5.js
1
2
3
4
5
6
7
8
9
var greeting = 'Hello JS!';

function greet() {
console.log(greeting);
}

module.exports = {
greet: greet
}
app.js
1
2
var greet5 = require('./greet5').greet;
greet5();
1
> Hello JS!

這裡我們使用object並把greet指向greet methond,所以module.exports只能存取greet而不會改到其它值
這種方式也稱為Revealing Module Pattern,在javascript中是常用的包裝方式。

以上為NodeJS使用module.exports的幾種方式。