Node.js 官方示例中的 ECMAScript 2015

全棧講師-金雲龍發表於2016-11-22

第一個 Node.js 的伺服器應用

Node.js 官方提供的幫助文件中,提供了一個非常適合入門的示例程式碼。可以幫助我們快速瞭解 Node.js 的主要作用。

1. 建立 example.js 檔案,並將官方幫助文件提供的程式碼進行貼上:
const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});
2. 開啟命令列視窗,輸入如下 node 命令,執行 example.js:
node example.js

需要注意的是:上述命令必須在命令列模式下,進入到 example.js 檔案所在的目錄。

命令執行成功後,在命令列視窗會看到如下效果:

3. 開啟瀏覽器,在位址列輸入命令列視窗提供的地址,訪問 Node.js 服務:
http://127.0.0.1:3000

由於所有示例程式碼 Node.js 官方幫助文件提供了,所以執行演示的操作步驟非常簡單。但,我們不能忽略其中的一些細節。

仔細閱讀上述示例程式碼,我們會發現其中使用了很多有關 ECMAScript 2015 規範中的新內容。那接下來,就讓我們一一來了解一下吧。

constletvar 的區別

首先,我們可以發現,在 Node.js 的官方幫助文件提供的示例程式碼中,大量地使用了 const 關鍵字。

const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer()

const 關鍵字是 ECMAScript 2015 規範中的新內容,是用來定義常量的。在 ECMAScript 2015 規範中還新增了 let 關鍵字,來替換原本的 var 關鍵字。

constletvar 關鍵字,都是用來在 JavaScript 中定義變數的。

1. 關於 JavaScript 的變數

變數是具有名字儲存資料資訊的容器。在程式碼中,使用變數名為值命名,需要遵守一定的規則。

值得注意的是:

  • 在 JavaScript 程式碼中,必須先宣告一個變數,這個變數才能被使用。
  • JavaScript 中的變數是弱型別的,也稱之為鬆散型別的。所謂弱型別/鬆散型別就是可以用來儲存任何型別的資料。

    var v = 100; v = "string";

2. 變數的宣告問題

1)重複宣告

使用 var 關鍵字重複宣告變數是合法且無害的。但是如果重複宣告並初始化的,這就表示重複宣告並初始化。由於 JavaScript 變數只能儲存一個資料,之前儲存的資料會被覆蓋。

var msg = "this is message";// 值為 this is message
var msg = 100;// 值為 100
2)遺漏宣告
  • 直接讀取一個沒有宣告的變數的值,JavaScript 會報錯。

    console.log(str);

上述示例程式碼,直接讀取了一個名為 str 的變數,但該變數並沒有宣告。所以,JavaScript 會報如下錯誤:

ReferenceError: str is not defined
  • 為一個沒有宣告的變數初始化,是合法的,但並不推薦這樣使用。
3)宣告提前

JavaScript 變數的另一特別之處是,你可以引用稍後宣告的變數,而不會引發異常。這一概念稱為變數宣告提升。

console.log( msg );// 不會報錯,輸出 undefined
var msg = "this is message";// 定義全域性變數 msg
console.log( msg );// 輸出 this is message

上述程式碼中的第一行輸出不會報錯,而是輸出 undefined值。效果等同於如下述程式碼:

var msg;// 定義全域性變數 msg,但未初始化
console.log( msg );// 不會報錯,輸出 undefined
msg = "this is message";// 初始化全域性變數 msg
console.log( msg );// 輸出 this is message
3. let 是更完美的 var
1)let 擁有塊級作用域

在 ECMAScript 2015 規範釋出之前,JavaScript 只存在全域性作用域和函式作用域。

var v1 = 'this is global variable';

function fn(){
  var v2 = 'this is function variable';

  console.log('v1 in function scope: '+v1);
  console.log('v2 in function scope: '+v2);
}

fn();// 在函式作用域中呼叫全域性變數和區域性變數
// 在全域性作用域中呼叫全域性變數和區域性變數
console.log('v1 in global scope: '+v1);
console.log('v2 in global scope: '+v2);

上述示例程式碼,執行時會報如下錯誤:

v1in function scope: this is global variable
v2in function scope: this is function variable
v1in global scope: this is global variable

scope.js:13
console.log('v2in global scope: '+v2);
                                ^
ReferenceError: v2 is not defined

上述報錯的原因在於 v2 變數是被定義在 fn 函式中,是區域性變數,並不能在全域性作用域被呼叫。

接下來,我們再看另外一個示例:

for(var i=0;i<=9;i++){
  console.log('use var define variable i in function scope: '+i);
}
console.log('use var define variable i in global scope: '+i);

上述示例程式碼的執行結果如下:

上述結果表明,在 for 迴圈語句中定義的 i 變數是一個全域性變數,因為在 ECMAScript 2015 之前,JavaScript 並不存在塊級作用域。

而將上述示例程式碼中,定義 i 變數的關鍵字 var 改成 let,會有什麼變化呢?

for(let i=0;i<=9;i++){
  console.log('use var define variable i in function scope: '+i);
}
console.log('use var define variable i in global scope: '+i);

上述修改過的示例程式碼,執行時會報如下錯誤:

use var define variable i in function scope: 0
use var define variable i in function scope: 1
use var define variable i in function scope: 2
use var define variable i in function scope: 3
use var define variable i in function scope: 4
use var define variable i in function scope: 5
use var define variable i in function scope: 6
use var define variable i in function scope: 7
use var define variable i in function scope: 8
use var define variable i in function scope: 9
/Users/king/node_and_mongoDB_in_action/01_first_node_demo/block_scope.js:10
console.log('use var define variable i in global scope: '+i);
                                                          ^
ReferenceError: i is not defined

根據上述報錯資訊,我們可以知道在 for 迴圈外列印的 i 變數未定義。換句話講,就說明 i 變數只作用於 for 迴圈語句內部,而不是全域性作用域。

像上述示例中的 i 變數的作用域,我們就可以稱之為 塊級作用域

2)let 不存在宣告提前

letvar 的第二個區別在於,使用 let 關鍵字宣告的變數,是不存在宣告提前的。

console.log( msg );
let msg = "this is message";
console.log( msg );

上述示例程式碼,執行是回報如下錯誤:

ReferenceError: msg is not defined

根據上述報錯資訊,表示使用 let 定義變數時,必須先宣告,後呼叫。

3) let 不允許重複宣告

letvar 的第三個區別在於,使用 let 關鍵字宣告的變數,是不允許重複宣告的。

let msg = "this is message";
console.log( msg );
let msg = "this is msg too";
console.log(msg);

上述示例程式碼,執行時會報如下錯誤:

SyntaxError: Identifier 'msg' has already been declared

根據上述報錯資訊,表示使用 let 定義變數時,只允許宣告一次,不能重複宣告。

4. const 定義常量

const宣告的變數只可以在宣告時賦值,不可隨意修改。

1)const 宣告時必須賦值
const theFairest;

上述示例程式碼,執行時會報如下錯誤:

SyntaxError: Missing initializer in const declaration
2) const 定義的值不能改變
// 定義常量MY_FAV並賦值7
const MY_FAV = 7;

// 在 Firefox 和 Chrome 這會失敗但不會報錯(在 Safari這個賦值會成功)
MY_FAV = 20;
console.log(MY_FAV); // 輸出 7
const MY_FAV = 20; // 嘗試重新宣告會報錯
var MY_FAV = 20;// MY_FAV 保留給上面的常量,這個操作會失敗
console.log(MY_FAV);// MY_FAV 依舊為7

上述示例程式碼,執行時會報如下錯誤:

SyntaxError: Identifier 'MY_FAV' has already been declared

箭頭函式

在 Node.js 的官方幫助文件提供的示例程式碼中,我們可以看到如下形式的函式:

http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

上述示例中的函式形式,看起來很怪異,我們將其進行改寫:

const server = http.createServer(function(req, res){
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});

server.listen(port, hostname, function(){
  console.log(`Server running at http://${hostname}:${port}/`);
});

進行改寫後的程式碼,是否更熟悉一些呢。那 Node.js 官方幫助文件中提供的示例程式碼裡使用的又是什麼呢?

1. 定義無參的箭頭函式

在 ECMAScript 5 之前,我們定義一個無參函式是這樣的:

var fn = function(){
    return 'this is function';
}

而在 ECMAScript 2015 之後,我們可以利用箭頭函式定義是這樣的:

var fn = () => 'this is function';

上述兩個函式的定義是等價的。

2. 定義帶參的箭頭函式

如果想要定義帶有引數的箭頭函式,可以如下方式:

var fn = v => v;

上述程式碼等同於如下:

var fn = function(v){
return v;
}

如果定義帶有多個引數的箭頭函式,可以將引數通過圓括號進行包裹。

var sum = (num1, num2) => num1 + num2;

上述程式碼等同於如下:

var sum = function(num1, num2) {
  return num1 + num2;
}

3. 箭頭函式體包含多條語句

上述示例程式碼中,我們只在箭頭函式中定義了一條語句。那想要定義多條語句的話,可以將所有函式體內的語句通過大括號進行包裹。

var sum = (num1, num2) => {
if(num1 < num2){
    return num1;
}else{
    return num2;
}
}

上述程式碼等同於如下:

var sum = function(num1, num2) {
if(num1 < num2){
    return num1;
}else{
    return num2;
}
}

大括號會被解析為程式碼塊。如果箭頭函式想要返回的是複雜資料(例如物件),需要使用圓括號進行包裹。

var me = () => ({ name: "longestory" });

上述程式碼等同於如下:

var me = function() {
return { name: "longestory" };
}

4. 箭頭函式的作用

通過上述內容,我們已經基本掌握了箭頭函式的用法。那箭頭函式究竟會有什麼作用呢?我們再回過頭來看看 Node.js 官方幫助文件的示例程式碼。

// ECMAScript 5 中的寫法
http.createServer(function(req, res){
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});

上述示例程式碼中,我們可以知道,通過 http 物件呼叫了 createServer 方法的同時向該方法傳遞了一個回撥函式。

// ECMAScript 2015 中的寫法
http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});

所以,箭頭函式的主要用法之一,就是用來簡化回撥函式的使用。

模板字串

在 Node.js 的官方幫助文件提供的示例程式碼中,我們還看到一行比較特殊的程式碼。

console.log(`Server running at http://${hostname}:${port}/`);

上述示例程式碼如果被改寫成這樣,相信你會更熟悉。

console.log('Server running at http://'+hostname+':'+port+'/');

實際上,在上述程式碼中,其實是使用了 JavaScript 的字串拼串。而 Node.js 的官方幫助文件中的示例程式碼,則使用 ECMAScript 2015 規範中的 模板字串

1. 模板字串的基本使用

模板字串(template string)是增強版的字串,用反引號(`)標識。

console.log(`this is a string.`);

上述示例程式碼的輸出結果如下:

this is a string.

你會發現上述示例程式碼的輸出結果與 ECMAScript 5 中的普通字串並沒有任何區別。

console.log('this is a string.');

但,如果我們想要輸出的字串很複雜,或者是多行的。那 ECMAScript 5 中的寫法應該是這樣的:

$('#list').html(
'<ul>'+
  '<li>first</li>'+
  '<li>second</li>'+
'</ul>'
);

而使用 ECMAScript 2015 規範中的模板字串,我們就可以寫成這樣:

$('#list').html(`
<ul>
  <li>first</li>
  <li>second</li>
</ul>
`);

2. 模板字串中使用變數

如果輸出的是一些文字加上變數的內容的話,在 ECMAScript 5 中的寫法是這樣的:

const hostname = '127.0.0.1';
const port = 3000;
console.log('Server running at http://'+hostname+':'+port+'/');

也就是說,我們在實際開發中,需要大量的字串拼寫工作。這樣做的問題在於:

  • 工作量巨大
  • 比較容易出錯

而 ECMAScript 2015 規範中的模板字串,則允許嵌入變數。只需要將需要嵌入的變數通過 ${} 進行包裹即可。

const hostname = '127.0.0.1';
const port = 3000;
console.log(`Server running at http://${hostname}:${port}/`);

在模板字串中,甚至可以嵌入函式的呼叫。

function fn(){
return 'Hello';
}
console.log(`${fn()} World`);

上述示例程式碼執行的結果如下:

Hello World

3. 模板字串的注意事項

當然,模板字串在使用過程中,也需要注意一些問題。

如果模板字串中嵌入的變數沒有宣告,則會報錯。

console.log(`Server running at http://${hostname}/`);

上述示例程式碼,執行後會報如下錯誤:

ReferenceError: hostname is not defined

相關文章