JavaScript 基礎:ES6+ 現代語法總整理

2026-01-26 08:03 | By justin | JavaScript
(Updated: 2026-01-26 08:03)

JavaScript 基礎:ES6+ 現代語法總整理

各位好!

這篇是 JavaScript 系列的第十二單元,也是最後一個單元。我們會總結所有 ES6 之後引入的現代語法特性。


ES6(ES2015)

let 和 const

// var 的問題
var x = 1;
var x = 2;  // 可以重複宣告
console.log(x);  // 2

if (true) {
  var y = 3;
}
console.log(y);  // 3(沒有區塊作用域)

// let:區塊作用域、不可重複宣告
let a = 1;
// let a = 2;  // 錯誤

if (true) {
  let b = 3;
}
// console.log(b);  // 錯誤:b is not defined

// const:不可重新賦值
const PI = 3.14;
// PI = 3.14159;  // 錯誤

// 但物件和陣列的內容可以修改
const obj = { name: "Alice" };
obj.name = "Bob";  // 可以
// obj = {};  // 錯誤

const arr = [1, 2, 3];
arr.push(4);  // 可以
// arr = [];  // 錯誤

箭頭函式

// 傳統函式
function add(a, b) {
  return a + b;
}

// 箭頭函式
const add = (a, b) => {
  return a + b;
};

// 簡化版(單行自動回傳)
const add = (a, b) => a + b;

// 單一參數可省略括號
const square = x => x * x;

// 無參數需要空括號
const greet = () => "Hello";

// 回傳物件需要加括號
const createUser = (name, age) => ({ name, age });

// this 的差異
const obj = {
  name: "Alice",
  // 傳統函式的 this 指向 obj
  greet1: function() {
    console.log(this.name);
  },
  // 箭頭函式的 this 指向外層
  greet2: () => {
    console.log(this.name);  // undefined
  }
};

模板字串

// 傳統字串拼接
const name = "Alice";
const age = 25;
const message = "My name is " + name + ", I'm " + age + " years old.";

// 模板字串
const message = `My name is ${name}, I'm ${age} years old.`;

// 多行字串
const html = `
  <div>
    <h1>${name}</h1>
    <p>Age: ${age}</p>
  </div>
`;

// 表達式
const price = 100;
const quantity = 3;
const total = `Total: $${price * quantity}`;

// 函式呼叫
const upperName = `Name: ${name.toUpperCase()}`;

// 巢狀模板
const nested = `Outer ${`Inner ${name}`}`;

解構賦值

// 陣列解構
const numbers = [1, 2, 3, 4, 5];
const [first, second] = numbers;
console.log(first, second);  // 1 2

// 跳過元素
const [a, , c] = numbers;  // 1 3

// 剩餘元素
const [x, y, ...rest] = numbers;
console.log(rest);  // [3, 4, 5]

// 預設值
const [p = 0, q = 0] = [1];
console.log(p, q);  // 1 0

// 交換變數
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b);  // 2 1

// 物件解構
const user = {
  name: "Alice",
  age: 25,
  email: "[email protected]"
};

const { name, age } = user;
console.log(name, age);  // Alice 25

// 重新命名
const { name: userName, age: userAge } = user;

// 預設值
const { phone = "未提供" } = user;

// 巢狀解構
const data = {
  user: {
    name: "Bob",
    address: {
      city: "Taipei"
    }
  }
};

const { user: { name, address: { city } } } = data;

// 函式參數解構
function printUser({ name, age = 0 }) {
  console.log(`${name}, ${age} 歲`);
}

printUser({ name: "Charlie", age: 30 });

預設參數

// ES5
function greet(name, greeting) {
  greeting = greeting || "Hello";
  return greeting + ", " + name;
}

// ES6
function greet(name, greeting = "Hello") {
  return `${greeting}, ${name}`;
}

console.log(greet("Alice"));           // "Hello, Alice"
console.log(greet("Bob", "Hi"));       // "Hi, Bob"

// 預設值可以是表達式
function createId(prefix = "ID", number = Date.now()) {
  return `${prefix}-${number}`;
}

// 預設值可以參考前面的參數
function calculate(a, b = a * 2) {
  return a + b;
}

console.log(calculate(5));     // 15 (5 + 10)
console.log(calculate(5, 3));  // 8 (5 + 3)

剩餘參數和展開運算子

// 剩餘參數(Rest Parameters)
function sum(...numbers) {
  return numbers.reduce((total, n) => total + n, 0);
}

console.log(sum(1, 2, 3, 4));  // 10

// 必須是最後一個參數
function test(first, second, ...rest) {
  console.log(first);   // 1
  console.log(second);  // 2
  console.log(rest);    // [3, 4, 5]
}

test(1, 2, 3, 4, 5);

// 展開運算子(Spread Operator)
// 陣列
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];  // [1, 2, 3, 4, 5, 6]

// 複製陣列
const original = [1, 2, 3];
const copy = [...original];

// 物件
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const merged = { ...obj1, ...obj2 };  // { a: 1, b: 2, c: 3, d: 4 }

// 複製物件
const user = { name: "Alice", age: 25 };
const userCopy = { ...user };

// 函式呼叫
const numbers = [1, 5, 3, 9, 2];
console.log(Math.max(...numbers));  // 9

簡寫屬性和方法

// 屬性簡寫
const name = "Alice";
const age = 25;

// ES5
const user1 = {
  name: name,
  age: age
};

// ES6
const user2 = { name, age };

// 方法簡寫
const calculator = {
  // ES5
  add: function(a, b) {
    return a + b;
  },
  // ES6
  subtract(a, b) {
    return a - b;
  }
};

// 計算屬性名稱
const key = "username";
const obj = {
  [key]: "Alice",
  ["get" + key.charAt(0).toUpperCase() + key.slice(1)]() {
    return this[key];
  }
};

console.log(obj.username);        // "Alice"
console.log(obj.getUsername());   // "Alice"

類別(Class)

// ES5 建構函式
function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.greet = function() {
  return `Hello, I'm ${this.name}`;
};

// ES6 類別
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    return `Hello, I'm ${this.name}`;
  }

  // Getter
  get info() {
    return `${this.name}, ${this.age} 歲`;
  }

  // Setter
  set birthYear(year) {
    this.age = new Date().getFullYear() - year;
  }

  // 靜態方法
  static create(name, age) {
    return new Person(name, age);
  }
}

const person = new Person("Alice", 25);
console.log(person.greet());
console.log(person.info);

// 繼承
class Student extends Person {
  constructor(name, age, grade) {
    super(name, age);  // 呼叫父類別建構函式
    this.grade = grade;
  }

  greet() {
    return `${super.greet()}, I'm a student`;
  }

  study() {
    return `${this.name} is studying`;
  }
}

const student = new Student("Bob", 20, "A");
console.log(student.greet());
console.log(student.study());

Promise

// 建立 Promise
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve("成功");
    } else {
      reject("失敗");
    }
  }, 1000);
});

// 使用 Promise
promise
  .then(result => console.log(result))
  .catch(error => console.error(error))
  .finally(() => console.log("完成"));

// Promise 鏈
fetch("/api/user")
  .then(response => response.json())
  .then(data => {
    console.log(data);
    return fetch(`/api/posts/${data.id}`);
  })
  .then(response => response.json())
  .then(posts => console.log(posts))
  .catch(error => console.error(error));

// Promise.all
Promise.all([
  fetch("/api/users"),
  fetch("/api/posts"),
  fetch("/api/comments")
])
  .then(responses => Promise.all(responses.map(r => r.json())))
  .then(([users, posts, comments]) => {
    console.log({ users, posts, comments });
  });

模組

// 匯出
export const PI = 3.14159;
export function add(a, b) {
  return a + b;
}

// 預設匯出
export default class Calculator {
  // ...
}

// 匯入
import Calculator, { PI, add } from "./math.js";
import * as math from "./math.js";

ES2016(ES7)

Array.prototype.includes()

const numbers = [1, 2, 3, 4, 5];

// ES5
console.log(numbers.indexOf(3) !== -1);  // true

// ES7
console.log(numbers.includes(3));  // true
console.log(numbers.includes(6));  // false

// 可以搜尋 NaN
console.log([NaN].includes(NaN));  // true

// 第二個參數指定起始位置
console.log(numbers.includes(3, 3));  // false(從索引 3 開始找)

指數運算子

// ES5
console.log(Math.pow(2, 3));  // 8

// ES7
console.log(2 ** 3);  // 8
console.log(3 ** 2);  // 9

// 組合使用
let x = 2;
x **= 3;  // x = x ** 3
console.log(x);  // 8

ES2017(ES8)

async/await

// Promise 寫法
function fetchData() {
  return fetch("/api/data")
    .then(response => response.json())
    .then(data => {
      console.log(data);
      return data;
    })
    .catch(error => console.error(error));
}

// async/await 寫法
async function fetchData() {
  try {
    const response = await fetch("/api/data");
    const data = await response.json();
    console.log(data);
    return data;
  } catch (error) {
    console.error(error);
  }
}

// 平行執行
async function fetchMultiple() {
  const [users, posts] = await Promise.all([
    fetch("/api/users").then(r => r.json()),
    fetch("/api/posts").then(r => r.json())
  ]);

  return { users, posts };
}

Object.entries() / Object.values()

const user = {
  name: "Alice",
  age: 25,
  city: "Taipei"
};

// Object.keys()(ES5)
console.log(Object.keys(user));
// ["name", "age", "city"]

// Object.values()
console.log(Object.values(user));
// ["Alice", 25, "Taipei"]

// Object.entries()
console.log(Object.entries(user));
// [["name", "Alice"], ["age", 25], ["city", "Taipei"]]

// 遍歷
for (const [key, value] of Object.entries(user)) {
  console.log(`${key}: ${value}`);
}

// 轉換物件
const doubled = Object.fromEntries(
  Object.entries({ a: 1, b: 2 }).map(([k, v]) => [k, v * 2])
);
console.log(doubled);  // { a: 2, b: 4 }

String padding

// padStart
console.log("5".padStart(3, "0"));     // "005"
console.log("ABC".padStart(10, "X"));  // "XXXXXXXABC"

// padEnd
console.log("5".padEnd(3, "0"));       // "500"
console.log("ABC".padEnd(10, "X"));    // "ABCXXXXXXX"

// 實用範例:格式化日期
const month = "3";
const day = "7";
const formatted = `2024-${month.padStart(2, "0")}-${day.padStart(2, "0")}`;
console.log(formatted);  // "2024-03-07"

ES2018(ES9)

物件的剩餘/展開屬性

// 剩餘屬性
const user = {
  name: "Alice",
  age: 25,
  email: "[email protected]",
  city: "Taipei"
};

const { name, age, ...rest } = user;
console.log(name);  // "Alice"
console.log(age);   // 25
console.log(rest);  // { email: "...", city: "..." }

// 展開屬性
const defaults = { theme: "light", lang: "en" };
const userPrefs = { lang: "zh", fontSize: 14 };
const config = { ...defaults, ...userPrefs };
console.log(config);
// { theme: "light", lang: "zh", fontSize: 14 }

// 複製物件
const original = { a: 1, b: 2 };
const copy = { ...original };

Promise.finally()

fetch("/api/data")
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error(error))
  .finally(() => {
    console.log("無論成功或失敗都會執行");
    // 清理資源、隱藏載入動畫等
  });

非同步迭代

// 一般迭代器
const iterable = {
  [Symbol.iterator]() {
    let i = 0;
    return {
      next() {
        return i < 3 ? { value: i++, done: false } : { done: true };
      }
    };
  }
};

for (const value of iterable) {
  console.log(value);  // 0, 1, 2
}

// 非同步迭代器
const asyncIterable = {
  [Symbol.asyncIterator]() {
    let i = 0;
    return {
      async next() {
        await new Promise(resolve => setTimeout(resolve, 1000));
        return i < 3 ? { value: i++, done: false } : { done: true };
      }
    };
  }
};

(async () => {
  for await (const value of asyncIterable) {
    console.log(value);  // 0, 1, 2(每秒一個)
  }
})();

ES2019(ES10)

Array.prototype.flat() / flatMap()

// flat() - 扁平化陣列
const nested = [1, [2, 3], [4, [5, 6]]];
console.log(nested.flat());        // [1, 2, 3, 4, [5, 6]]
console.log(nested.flat(2));       // [1, 2, 3, 4, 5, 6]
console.log(nested.flat(Infinity)); // 完全扁平化

// flatMap() - map 後扁平化
const numbers = [1, 2, 3];
console.log(numbers.map(x => [x, x * 2]));
// [[1, 2], [2, 4], [3, 6]]

console.log(numbers.flatMap(x => [x, x * 2]));
// [1, 2, 2, 4, 3, 6]

// 實用範例:拆分句子成單字
const sentences = ["Hello World", "JavaScript is fun"];
const words = sentences.flatMap(s => s.split(" "));
console.log(words);
// ["Hello", "World", "JavaScript", "is", "fun"]

Object.fromEntries()

// 陣列轉物件
const entries = [["name", "Alice"], ["age", 25]];
const obj = Object.fromEntries(entries);
console.log(obj);  // { name: "Alice", age: 25 }

// Map 轉物件
const map = new Map([
  ["name", "Bob"],
  ["age", 30]
]);
const obj2 = Object.fromEntries(map);

// 實用範例:過濾物件屬性
const user = { name: "Alice", age: 25, password: "secret" };
const publicUser = Object.fromEntries(
  Object.entries(user).filter(([key]) => key !== "password")
);
console.log(publicUser);  // { name: "Alice", age: 25 }

String.prototype.trimStart() / trimEnd()

const str = "   Hello World   ";

console.log(str.trim());       // "Hello World"
console.log(str.trimStart());  // "Hello World   "
console.log(str.trimEnd());    // "   Hello World"

// 別名
console.log(str.trimLeft());   // 同 trimStart
console.log(str.trimRight());  // 同 trimEnd

ES2020(ES11)

Optional Chaining

const user = {
  name: "Alice",
  address: {
    city: "Taipei"
  }
};

// 傳統寫法
const city = user && user.address && user.address.city;

// Optional Chaining
const city = user?.address?.city;
console.log(city);  // "Taipei"

// 不存在時回傳 undefined
const zip = user?.address?.zip;
console.log(zip);  // undefined

// 陣列
const firstItem = arr?.[0];

// 函式
const result = obj.method?.();

// 實用範例
function getCity(user) {
  return user?.address?.city ?? "未知";
}

Nullish Coalescing

// || 運算子的問題
const value1 = 0 || "default";
console.log(value1);  // "default"(0 被視為 falsy)

const value2 = "" || "default";
console.log(value2);  // "default"(空字串被視為 falsy)

// ?? 運算子只處理 null 和 undefined
const value3 = 0 ?? "default";
console.log(value3);  // 0

const value4 = "" ?? "default";
console.log(value4);  // ""

const value5 = null ?? "default";
console.log(value5);  // "default"

const value6 = undefined ?? "default";
console.log(value6);  // "default"

// 實用範例
function createUser(options = {}) {
  return {
    name: options.name ?? "匿名",
    age: options.age ?? 0,
    active: options.active ?? true
  };
}

BigInt

// Number 的限制
console.log(Number.MAX_SAFE_INTEGER);  // 9007199254740991

// BigInt
const big1 = 9007199254740991n;
const big2 = BigInt("9007199254740991");

// 運算
console.log(big1 + 1n);  // 9007199254740992n
console.log(big1 * 2n);  // 18014398509481982n

// 不能混用 BigInt 和 Number
// console.log(big1 + 1);  // 錯誤

// 轉換
console.log(Number(big1));  // 9007199254740991
console.log(big1.toString());  // "9007199254740991"

Promise.allSettled()

const promises = [
  Promise.resolve(1),
  Promise.reject("錯誤"),
  Promise.resolve(3)
];

// Promise.all() - 有一個失敗就全部失敗
Promise.all(promises)
  .then(results => console.log(results))
  .catch(error => console.log(error));  // "錯誤"

// Promise.allSettled() - 等待全部完成
Promise.allSettled(promises)
  .then(results => {
    console.log(results);
    // [
    //   { status: "fulfilled", value: 1 },
    //   { status: "rejected", reason: "錯誤" },
    //   { status: "fulfilled", value: 3 }
    // ]
  });

globalThis

// 統一的全域物件
// 瀏覽器:window
// Node.js:global
// Web Workers:self

// ES2020:globalThis 在所有環境都指向全域物件
console.log(globalThis);

// 實用範例
function getGlobalObject() {
  return globalThis;
}

ES2021(ES12)

String.prototype.replaceAll()

const str = "Hello World, Hello JavaScript";

// replace() 只替換第一個
console.log(str.replace("Hello", "Hi"));
// "Hi World, Hello JavaScript"

// replaceAll() 替換所有
console.log(str.replaceAll("Hello", "Hi"));
// "Hi World, Hi JavaScript"

// 正則表達式
console.log(str.replaceAll(/Hello/g, "Hi"));

Promise.any()

const p1 = new Promise((resolve, reject) => 
  setTimeout(() => reject("錯誤 1"), 100)
);
const p2 = new Promise((resolve, reject) => 
  setTimeout(() => resolve("成功"), 200)
);
const p3 = new Promise((resolve, reject) => 
  setTimeout(() => reject("錯誤 2"), 300)
);

// 回傳第一個成功的
Promise.any([p1, p2, p3])
  .then(result => console.log(result))  // "成功"
  .catch(error => console.log(error));

// 如果全部失敗
Promise.any([
  Promise.reject("錯誤 1"),
  Promise.reject("錯誤 2")
])
  .catch(error => {
    console.log(error);  // AggregateError
    console.log(error.errors);  // ["錯誤 1", "錯誤 2"]
  });

邏輯賦值運算子

// &&=
let x = 1;
x &&= 2;  // x = x && 2
console.log(x);  // 2

// ||=
let y = 0;
y ||= 5;  // y = y || 5
console.log(y);  // 5

// ??=
let z = null;
z ??= 10;  // z = z ?? 10
console.log(z);  // 10

// 實用範例
const options = {};
options.timeout ??= 5000;  // 只在 undefined/null 時設定預設值

數字分隔符號

// 提高可讀性
const billion = 1_000_000_000;
const bytes = 0b1111_0000_1010_0101;
const hex = 0xFF_EC_DE_5E;
const price = 1_234_567.89;

console.log(billion);  // 1000000000
console.log(bytes);    // 61605

ES2022(ES13)

Top-level await

// 模組頂層可以使用 await
const data = await fetch("/api/data").then(r => r.json());
console.log(data);

// 條件式匯入
const module = await import(`./locale/${lang}.js`);

// 不需要包在 async 函式裡

Class Fields

class Counter {
  // 公開欄位
  count = 0;

  // 私有欄位
  #privateCount = 0;

  // 私有方法
  #increment() {
    this.#privateCount++;
  }

  // 靜態公開欄位
  static description = "計數器";

  // 靜態私有欄位
  static #instances = 0;

  constructor() {
    Counter.#instances++;
  }

  getPrivateCount() {
    return this.#privateCount;
  }
}

const counter = new Counter();
console.log(counter.count);  // 0
// console.log(counter.#privateCount);  // 錯誤:私有欄位

at() 方法

const arr = [1, 2, 3, 4, 5];

// 傳統方式取最後一個元素
console.log(arr[arr.length - 1]);  // 5

// at() 方法
console.log(arr.at(-1));   // 5(最後一個)
console.log(arr.at(-2));   // 4(倒數第二個)
console.log(arr.at(0));    // 1(第一個)

// 字串也支援
const str = "Hello";
console.log(str.at(-1));   // "o"

實用總結

現代 JavaScript 最佳實踐

// 1. 使用 const 和 let
const PI = 3.14159;
let count = 0;

// 2. 箭頭函式
const add = (a, b) => a + b;

// 3. 模板字串
const message = `Hello, ${name}!`;

// 4. 解構
const { name, age } = user;
const [first, second] = array;

// 5. 展開運算子
const newArray = [...oldArray, newItem];
const newObject = { ...oldObject, newProp: value };

// 6. 簡寫屬性
const obj = { name, age };

// 7. async/await
async function fetchData() {
  const data = await fetch("/api").then(r => r.json());
  return data;
}

// 8. Optional Chaining
const city = user?.address?.city;

// 9. Nullish Coalescing
const value = input ?? defaultValue;

// 10. Array 方法
const doubled = numbers.map(n => n * 2);
const evens = numbers.filter(n => n % 2 === 0);
const sum = numbers.reduce((acc, n) => acc + n, 0);

小結

這篇我們總結了:

  • ES6 的核心特性(let/const、箭頭函式、類別等)
  • ES2016-ES2022 的新增功能
  • 現代 JavaScript 最佳實踐

恭喜你!

完成整個 JavaScript 系列後,你已經掌握:

  • JavaScript 基礎語法
  • 變數、資料型別、運算子
  • 函式與陣列操作
  • 物件與迴圈
  • 作用域與閉包
  • 非同步程式設計
  • 模組系統
  • DOM 操作
  • 所有現代 ES6+ 語法

接下來可以:

  • 學習 Next.js 系列 進入網頁開發
  • 學習更多前端框架(React、Vue)
  • 練習實作專案累積經驗
  • 學習 TypeScript 增強型別安全

祝你學習順利!


0 留言

目前沒有留言

發表留言
回覆