# 給進入 Vue.js 前的 ES6 必備知識

由於 Vue 3.0 使用到大量 ES6 相關語法,若讀者不夠熟悉 ES6,在學習的過程難免會感到迷惑。 建議讀者可以先閱讀本篇 ES6 的快速基礎介紹,雖然不會講得太詳細,但可幫助你更容易進入狀況。

# varletconst

varletconst 在 JavaScript 都是用來宣告變數的語法,最大的差別是它們的 scope (變數有效範圍) 的不同。 切分 var 作用範圍的最小單位為 function,而 letconst 的作用範圍是 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 };
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();
1
2
3
4
5
6
7
8
9

當然在 export 也可以不用給變數名稱:

// --- c.js
export default function () {
  console.log('Hello 008 JS!!!');
}
1
2
3
4

在另一個檔案就可以這樣來使用:

// --- d.js
import greeting from "./c.js";

// 'Hello 008 JS!!!'
greeting();
1
2
3
4
5

# 箭頭函數與 this

從 ES6 開始新增了一種叫做 「箭頭函式表示式」 (Arrow Function expression) 的函式表達式。 快速看一下,如何將一般的函式轉換成箭頭函式的寫法:

const plus = function (numA, numB) {
  return numA + numB;
};
1
2
3

首先我們把參數往前提,然後把關鍵字 function 刪掉改成箭頭符號 =>

const plus = (numA, numB) => {
  return numA + numB;
};
1
2
3

如果這個函式只是想要回傳某個運算結果的時候,可以將 return 以及大括號 { } 省略:

const plus = (numA, numB) => numA + numB;
1

而只有一個參數的時候,參數前面的小括號 () 則可以省略:

const saySomething = msg => console.log(msg);

// "Hello!"
saySomething('Hello!');
1
2
3
4

另外需要注意的是,在箭頭函式使用 this 時,這時 this 會指向箭頭函式外面的 this, 這個規則與原本 function 所宣告的函式不同,而且箭頭函式無法透過 bind() 強制指定裡面的 this

# 字串模板 (Template literals)

以往我們在組合 JavaScript 的變數與 HTML 模板的時候,大多會透過「字串結合」 + 的模式,或透過陣列來新增字串,最後再用 [].join("") 的方式串接起來。 但自 ES6 起,我們可以透過字串模板的語法,將變數、運算式等插入至我們的網頁模版當中,像這樣:

// 用「`...`」取代單/雙引號
`string text ${ expression } string text`
1
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);
1
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);
1
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);
1
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);
1
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 } 
1
2
3
4
5
6
7
8
9

要注意的是,其餘運算子所分離的部分只是陣列或物件的「淺拷貝」 (shallow-copy),若在多層物件使用時要特別小心。

# Promise 物件,asyncawait

為了解決過去同步與非同步的問題,ES6 提供了 Promise 物件:

const myPromiseFunc = new Promise((resolve, reject) => {
  resolve(someValue);         // 完成
  reject("failure reason");   // 拒絕
});
1
2
3
4

Promise 的任務被完成的時候,我們就可以呼叫 resolve(),然後將取得的資料傳遞出去。 或是說想要拒絕這個 Promise ,那麼就裡面呼叫 reject() 來拒絕它。

function myAsyncFunction(url) {
  return new Promise((resolve, reject) => {
    // resolve() or reject()
  });
};

// 透過 .then() 來取代過去的 callback hell
myAsyncFunction(...)
  .then(() => { ... });
1
2
3
4
5
6
7
8
9

# asyncawait

再後來,從 Promise 物件又延伸出 asyncawait 兩個新特性,其實本質上是更簡便的語法糖。

假設我們有兩個非同步任務要處理,並且我們希望在 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();
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

像這樣,透過 asyncawait 我們就可以擺脫過去一層層 callback 的噩夢,程式碼也更加簡潔。

Last Updated: 1/14/2021, 6:54:18 PM