# 給進入 Vue.js 前的 ES6 必備知識
由於 Vue 3.0 使用到大量 ES6 相關語法,若讀者不夠熟悉 ES6,在學習的過程難免會感到迷惑。 建議讀者可以先閱讀本篇 ES6 的快速基礎介紹,雖然不會講得太詳細,但可幫助你更容易進入狀況。
# var
、 let
與 const
var
、 let
與 const
在 JavaScript 都是用來宣告變數的語法,最大的差別是它們的 scope (變數有效範圍) 的不同。
切分 var
作用範圍的最小單位為 function
,而 let
與 const
的作用範圍是 block
也就是俗稱的大括號: { }
來切分。
const
所宣告的變數還有另一個特性是無法被重新賦值 (re-assign)。
# ES Module 與 import 、 export
JavaScript 自從 ES6 開始新增了模組系統 (ES Module) ,我們可以將每個 JavaScript 的檔案當作是一個獨立的模組來看待, 在 A 檔案匯出 (export) 在 B 檔案匯入 (import)。
// --- a.js
export const aString = 'This is A String';
export function aFunction(){
console.log('A function test')
}
export const aObject = { a: 1 };
2
3
4
5
6
7
8
// --- b.js
import { aString, aFunction, aObject } from './a.js';
// 'This is A String'
console.log(aString);
// { a: 1 }
console.log(aObject);
// A function test
aFunction();
2
3
4
5
6
7
8
9
當然在 export
也可以不用給變數名稱:
// --- c.js
export default function () {
console.log('Hello 008 JS!!!');
}
2
3
4
在另一個檔案就可以這樣來使用:
// --- d.js
import greeting from "./c.js";
// 'Hello 008 JS!!!'
greeting();
2
3
4
5
# 箭頭函數與 this
從 ES6 開始新增了一種叫做 「箭頭函式表示式」 (Arrow Function expression) 的函式表達式。 快速看一下,如何將一般的函式轉換成箭頭函式的寫法:
const plus = function (numA, numB) {
return numA + numB;
};
2
3
首先我們把參數往前提,然後把關鍵字 function
刪掉改成箭頭符號 =>
:
const plus = (numA, numB) => {
return numA + numB;
};
2
3
如果這個函式只是想要回傳某個運算結果的時候,可以將 return
以及大括號 { }
省略:
const plus = (numA, numB) => numA + numB;
而只有一個參數的時候,參數前面的小括號 ()
則可以省略:
const saySomething = msg => console.log(msg);
// "Hello!"
saySomething('Hello!');
2
3
4
另外需要注意的是,在箭頭函式使用 this
時,這時 this
會指向箭頭函式外面的 this
,
這個規則與原本 function
所宣告的函式不同,而且箭頭函式無法透過 bind()
強制指定裡面的 this
。
# 字串模板 (Template literals)
以往我們在組合 JavaScript 的變數與 HTML 模板的時候,大多會透過「字串結合」 +
的模式,或透過陣列來新增字串,最後再用 [].join("")
的方式串接起來。
但自 ES6 起,我們可以透過字串模板的語法,將變數、運算式等插入至我們的網頁模版當中,像這樣:
// 用「`...`」取代單/雙引號
`string text ${ expression } string text`
2
這樣我們就可以將這個 expression
所代表的運算式或數值置入到字串裡頭了。
# 解構賦值 (Destructuring assignment)
ES6 提供了解構賦值的語法,可以將陣列或者物件裡面的資料解開變成獨立的變數:
const user = {
id: 42,
displayName: 'jdoe',
fullName: {
firstName: 'John',
lastName: 'Doe'
}
};
const { id, displayName, fullName } = user;
// 42
console.log(id);
// 'jdoe'
console.log(displayName);
// { firstName: 'John', lastName: 'Doe' }
console.log(fullName);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
除了物件以外,陣列也可以:
const number = [1, 2, 3, 4, 5];
const [x, y] = number;
// 1
console.log(x);
// 2
console.log(y);
2
3
4
5
6
7
8
9
# ...
展開運算子 (Spread Operator) / 其餘運算子 (Rest Operator)
雖然 ES6 提供的展開運算子與其餘運算子的語法都是 ...
,不過它們兩者所代表的涵意還是不太一樣。
# 展開運算子
展開運算子通常會用在陣列,或者是函式的參數,如:
const frameworks = ["Vue.js", "Angular", "React"]
const arr = ["Awesome", ...frameworks];
// ["Awesome", "Vue.js", "Angular", "React"]
console.log(arr);
2
3
4
5
像這樣,在新陣列中,就會將原本的 frameworks
陣列展開成個別元素,達到類似 Array.concat
的效果。
# 其餘運算子
延續前面的例子,我們可以透過 「其餘運算子」 將剩下的部分拆解出來:
// ["Awesome", "Vue.js", "Angular", "React"]
console.log(arr);
const [a, b, ...others] = arr;
// "Awesome"
console.log(a);
// "Vue.js"
console.log(b);
// ["Angular", "React"]
console.log(others);
2
3
4
5
6
7
8
9
10
11
像這樣,我們可以搭配解構賦值的語法,將 arr
陣列拆解處來,並將剩餘的元素透過 ...others
分離。
當然,使用在物件上也是可以的:
// Rest Properties
const { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }
console.log(x); // 1
console.log(y); // 2
console.log(z); // { a: 3, b: 4 }
// Spread Properties
const obj = { x, y, ...z };
console.log(obj) // { x: 1, y: 2, a: 3, b: 4 }
2
3
4
5
6
7
8
9
要注意的是,其餘運算子所分離的部分只是陣列或物件的「淺拷貝」 (shallow-copy),若在多層物件使用時要特別小心。
# Promise
物件,async
與 await
為了解決過去同步與非同步的問題,ES6 提供了 Promise
物件:
const myPromiseFunc = new Promise((resolve, reject) => {
resolve(someValue); // 完成
reject("failure reason"); // 拒絕
});
2
3
4
當 Promise
的任務被完成的時候,我們就可以呼叫 resolve()
,然後將取得的資料傳遞出去。
或是說想要拒絕這個 Promise
,那麼就裡面呼叫 reject()
來拒絕它。
function myAsyncFunction(url) {
return new Promise((resolve, reject) => {
// resolve() or reject()
});
};
// 透過 .then() 來取代過去的 callback hell
myAsyncFunction(...)
.then(() => { ... });
2
3
4
5
6
7
8
9
# async
與 await
再後來,從 Promise
物件又延伸出 async
與 await
兩個新特性,其實本質上是更簡便的語法糖。
假設我們有兩個非同步任務要處理,並且我們希望在 asyncFunc1
執行之後才去執行 asyncFunc2
:
function asyncFunc1(url) {
return new Promise((resolve, reject) => {
// resolve() or reject()
});
};
function asyncFunc2(url) {
return new Promise((resolve, reject) => {
// resolve() or reject()
});
};
const asyncCall = async () => {
const result1 = await asyncFunc1();
const result2 = await asyncFunc2();
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
像這樣,透過 async
與 await
我們就可以擺脫過去一層層 callback 的噩夢,程式碼也更加簡潔。