一、 Node.js
2009年,Ryan正式推出了基于JavaScript語(yǔ)言和V8引擎的開源Web服務(wù)器項(xiàng)目,命名為Node.js。雖然名字很土,但是,Node第一次把JavaScript帶入到后端服務(wù)器開發(fā)。
二、安裝Node.js和npm
由于Node.js平臺(tái)是在后端運(yùn)行JavaScript代碼,所以,必須首先在本機(jī)安裝Node環(huán)境。
npm其實(shí)是Node.js的包管理工具(package manager)。
請(qǐng)?jiān)诒緳C(jī)安裝Node.js環(huán)境,并確保node和npm能正常運(yùn)行。
node -v 查看node.js版本
npm -v 查看npm版本
三、第一個(gè)node程序
用文本編輯器寫JavaScript程序,然后保存為后綴為.js的文件,就可以用node直接運(yùn)行這個(gè)程序了。但是必須要以.js結(jié)尾。此外,文件名只能是英文字母、數(shù)字和下劃線的組合。
四、node --use_strict calc.js 讓Node直接為所有js文件開啟嚴(yán)格模式,不用在每一個(gè)文件開頭寫上’use strict’;
五、Node的交互模式和直接運(yùn)行.js文件(命令行模式)有什么區(qū)別呢?
直接輸入node進(jìn)入交互模式,相當(dāng)于啟動(dòng)了Node解釋器,但是等待你一行一行地輸入源代碼,每輸入一行就執(zhí)行一行。(node交互模式)
直接運(yùn)行node hello.js文件相當(dāng)于啟動(dòng)了Node解釋器,然后一次性把hello.js文件的源代碼給執(zhí)行了,你是沒有機(jī)會(huì)以交互的方式輸入源代碼的。(命令行模式)
六、Node程序開發(fā)IDE visual studio code
七、模塊
① 在Node環(huán)境中,一個(gè).js文件就稱之為一個(gè)模塊(module)。
② 使用模塊有什么好處?
最大的好處是大大提高了代碼的可維護(hù)性。其次,編寫代碼不必從零開始。當(dāng)一個(gè)模塊編寫完畢,就可以被其他地方引用。我們?cè)诰帉懗绦虻臅r(shí)候,也經(jīng)常引用其他模塊,包括Node內(nèi)置的模塊和來自第三方的模塊。
使用模塊還可以避免函數(shù)名和變量名沖突。相同名字的函數(shù)和變量完全可以分別存在不同的模塊中,因此,我們自己在編寫模塊時(shí),不必考慮名字會(huì)與其他模塊沖突。
在上一節(jié),我們編寫了一個(gè)hello.js文件,這個(gè)hello.js文件就是一個(gè)模塊,模塊的名字就是文件名(去掉.js后綴),所以hello.js文件就是名為hello的模塊。
③ CommonJS規(guī)范
這種模塊加載機(jī)制被稱為CommonJS規(guī)范。在這個(gè)規(guī)范下,每個(gè).js文件都是一個(gè)模塊,它們內(nèi)部各自使用的變量名和函數(shù)名都互不沖突,例如,hello.js和main.js都申明了全局變量var s = 'xxx',但互不影響。
一個(gè)模塊想要對(duì)外暴露變量(函數(shù)也是變量),可以用module.exports = variable;,一個(gè)模塊要引用其他模塊暴露的變量,用var ref = require('module_name');就拿到了引用模塊的變量。
④ 結(jié)論
要在模塊中對(duì)外輸出變量,用:
module.exports = variable;
輸出的變量可以是任意對(duì)象、函數(shù)、數(shù)組等等。
要引入其他模塊輸出的對(duì)象,用:
var foo = require('other_module');
引入的對(duì)象具體是什么,取決于引入模塊輸出的對(duì)象。
八、模塊原理:
① 模塊的隔離
當(dāng)我們編寫JavaScript代碼時(shí),我們可以申明全局變量:
var s = 'global';
在瀏覽器中,大量使用全局變量可不好。如果你在a.js中使用了全局變量s,那么,在b.js中也使用全局變量s,將造成沖突,b.js中對(duì)s賦值會(huì)改變a.js的運(yùn)行邏輯。
也就是說,JavaScript語(yǔ)言本身并沒有一種模塊機(jī)制來保證不同模塊可以使用相同的變量名。
那Node.js是如何實(shí)現(xiàn)這一點(diǎn)的?
其實(shí)要實(shí)現(xiàn)“模塊”這個(gè)功能,并不需要語(yǔ)法層面的支持。Node.js也并不會(huì)增加任何JavaScript語(yǔ)法。實(shí)現(xiàn)“模塊”功能的奧妙就在于JavaScript是一種函數(shù)式編程語(yǔ)言,它支持閉包。如果我們把一段JavaScript代碼用一個(gè)函數(shù)包裝起來,這段代碼的所有“全局”變量就變成了函數(shù)內(nèi)部的局部變量。
請(qǐng)注意我們編寫的hello.js代碼是這樣的:
var s = 'Hello';
var name = 'world';
console.log(s + ' ' + name + '!');
Node.js加載了hello.js后,它可以把代碼包裝一下,變成這樣執(zhí)行:
(function () {
// 讀取的hello.js代碼:
var s = 'Hello';
var name = 'world';
console.log(s + ' ' + name + '!');
// hello.js代碼結(jié)束
})();
這樣一來,原來的全局變量s現(xiàn)在變成了匿名函數(shù)內(nèi)部的局部變量。如果Node.js繼續(xù)加載其他模塊,這些模塊中定義的“全局”變量s也互不干擾。
所以,Node利用JavaScript的函數(shù)式編程的特性,輕而易舉地實(shí)現(xiàn)了模塊的隔離。
② 模塊的輸出
這個(gè)也很容易實(shí)現(xiàn),Node可以先準(zhǔn)備一個(gè)對(duì)象module:
// 準(zhǔn)備module對(duì)象:
var module = {
id: 'hello',
exports: {}
};
var load = function (module) {
// 讀取的hello.js代碼:
function greet(name) {
console.log('Hello, ' + name + '!');
}
module.exports = greet;
// hello.js代碼結(jié)束
return module.exports;
};
var exported = load(module);
// 保存module:
save(module, exported);
可見,變量module是Node在加載js文件前準(zhǔn)備的一個(gè)變量,并將其傳入加載函數(shù),我們?cè)趆ello.js中可以直接使用變量module原因就在于它實(shí)際上是函數(shù)的一個(gè)參數(shù):
module.exports = greet;
通過把參數(shù)module傳遞給load()函數(shù),hello.js就順利地把一個(gè)變量傳遞給了Node執(zhí)行環(huán)境,Node會(huì)把module變量保存到某個(gè)地方。
由于Node保存了所有導(dǎo)入的module,當(dāng)我們用require()獲取module時(shí),Node找到對(duì)應(yīng)的module,把這個(gè)module的exports變量返回,這樣,另一個(gè)模塊就順利拿到了模塊的輸出:
var greet = require('./hello');
以上是Node實(shí)現(xiàn)JavaScript模塊的一個(gè)簡(jiǎn)單的原理介紹
③ module.exports vs exports (有點(diǎn)不懂)
很多時(shí)候,你會(huì)看到,在Node環(huán)境中,有兩種方法可以在一個(gè)模塊中輸出變量:
方法一:對(duì)module.exports賦值:
// hello.js
function hello() {
console.log('Hello, world!');
}
function greet(name) {
console.log('Hello, ' + name + '!');
}
module.exports = {
hello: hello,
greet: greet
};
方法二:直接使用exports:
// hello.js
function hello() {
console.log('Hello, world!');
}
function greet(name) {
console.log('Hello, ' + name + '!');
}
function hello() {
console.log('Hello, world!');
}
exports.hello = hello;
exports.greet = greet;
但是你不可以直接對(duì)exports賦值:
// 代碼可以執(zhí)行,但是模塊并沒有輸出任何變量:
exports = {
hello: hello,
greet: greet
};
如果要輸出一個(gè)鍵值對(duì)象{},可以利用exports這個(gè)已存在的空對(duì)象{},并繼續(xù)在上面添加新的鍵值;
如果要輸出一個(gè)函數(shù)或數(shù)組,必須直接對(duì)module.exports對(duì)象賦值。
所以我們可以得出結(jié)論:直接對(duì)module.exports賦值,可以應(yīng)對(duì)任何情況:
module.exports = {
foo: function () { return 'foo'; }
};
或者:
module.exports = function () { return 'foo'; };
最終,我們強(qiáng)烈建議使用module.exports = xxx的方式來輸出模塊變量,這樣,你只需要記憶一種方法。