EP.02
JavaScript 入門系列

六個 ES6+ 語法,
讓你看懂所有 React 程式碼

箭頭函式、解構、展開、模板字串、import/export、選用鏈——一次全學完,從此 React 程式碼不再有看不懂的語法

J

Joseph Chen

2026·JavaScript 入門系列·10 min read·EP.02
ES6+
JavaScript
React 前置知識

「你學完基礎 JS,打開 React 程式碼,第一行就看到:const [count, setCount] = useState(0)—— 這個 [count, setCount] = 是什麼鬼語法?別緊張——這是 ES6 的解構賦值,這篇六個語法讀完你就全懂了。」

— ES6 是 2015 年發布的 JavaScript 大改版,現代 React 程式碼全都是這個語法

1

箭頭函式

() => {}
2

解構賦值

const { a } = obj
3

展開運算子

[...arr]
4

模板字串

`Hello ${name}`
5

import/export

import X from Y
6

選用鏈

obj?.key ?? default
1

箭頭函式 () => {}

箭頭函式就是函式的速記法——功能完全一樣,只是寫法更短。在 React 裡你到處都會看到它,特別是事件處理和 .map() 回呼。

arrow-functions.js
// 傳統函式寫法
function add(a, b) {
  return a + b;
}

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

// 進階省略:只有一行 return 時,可以拿掉 {} 和 return
const add = (a, b) => a + b;

// 只有一個參數時,小括號也可以省略
const double = n => n * 2;

// 沒有參數:小括號不能省
const greet = () => 'Hello!';
arrow-in-react.jsx
// React 裡最常見的箭頭函式用法

// 1. .map() 的回呼
const names = ['Alice', 'Bob', 'Charlie'];
const upper = names.map(name => name.toUpperCase());
// ['ALICE', 'BOB', 'CHARLIE']

// 2. 事件處理(onClick、onChange 等)
function MyButton() {
  return (
    <button onClick={() => console.log('被點了!')}>
      點我
    </button>
  );
}

// 3. Component 本身也可以用箭頭函式定義(跟 function 宣告等價)
const Greeting = ({ name }) => (
  <h1>你好,{name}</h1>
);

INFO

什麼時候用傳統函式、什麼時候用箭頭函式?
大多數情況下可以互換。React 社群偏好箭頭函式,因為更短、更容易閱讀。 唯一要注意的是:箭頭函式沒有自己的 this(這在 EP.03 閉包篇會講到)。
2

解構賦值——從快遞盒裡拆包裹

解構賦值就像「拆包裹」——從陣列或物件裡,把你要的東西直接取出來,並且立刻幫它命名。 這是 React 裡最高頻率出現的 ES6 語法,特別是 useState 和 props。

陣列解構——useState 的秘密

array-destructuring.js
// 不用解構:很囉嗦
const pair = [0, function setter(v) { /* 更新函式 */ }];
const count = pair[0];
const setCount = pair[1];

// 用解構:這就是 React 的 useState!
const [count, setCount] = useState(0);
// [count, setCount] 對應 useState 回傳陣列的 [第0個, 第1個]

// 更多陣列解構範例
const [first, second, ...rest] = [1, 2, 3, 4, 5];
console.log(first);   // 1
console.log(second);  // 2
console.log(rest);    // [3, 4, 5]  ← ...rest 收集剩下的

// 跳過某個值:用逗號佔位
const [,, third] = [10, 20, 30];
console.log(third);   // 30

物件解構——props 的秘密

object-destructuring.js
const user = { name: 'Joseph', age: 26, city: '高雄' };

// 不用解構:一行一行取出
const name = user.name;
const age = user.age;

// 用解構:一次取出多個屬性
const { name, age } = user;
console.log(name);  // 'Joseph'
console.log(age);   // 26

// 重新命名(變數名 : 新名字)
const { name: userName, city: location } = user;
console.log(userName);   // 'Joseph'
console.log(location);   // '高雄'

// 設定預設值(屬性不存在時用預設值)
const { name, role = '訪客' } = user;
console.log(role);  // '訪客'(user 沒有 role 屬性)

Component props 的解構

這是你在 React 裡最常看到的用法:在函式參數裡直接解構 props 物件。

props-destructuring.jsx
// 不用解構:要一直寫 props.xxx
function UserCard(props) {
  return (
    <div>
      <h2>{props.name}</h2>
      <p>{props.age} 歲 — {props.city}</p>
    </div>
  );
}

// 用解構:在參數位置直接拆開 props(React 偏好這種寫法)
function UserCard({ name, age, city }) {
  return (
    <div>
      <h2>{name}</h2>
      <p>{age} 歲 — {city}</p>
    </div>
  );
}

// 使用時一樣
<UserCard name="Joseph" age={26} city="高雄" />
3

展開運算子 ...

三個點 ...,叫做展開運算子(Spread Operator)。比喻:就像「複製貼上」——把一個陣列或物件的所有內容展開到另一個裡面。

spread-operator.js
// 複製陣列(產生全新陣列,不影響原本的)
const original = [1, 2, 3];
const copy = [...original];         // [1, 2, 3]
const added = [...original, 4, 5];  // [1, 2, 3, 4, 5]

// 合併兩個陣列
const a = [1, 2, 3];
const b = [4, 5, 6];
const merged = [...a, ...b];  // [1, 2, 3, 4, 5, 6]

// 複製物件(產生全新物件)
const user = { name: 'Joseph', age: 26 };
const copy = { ...user };                   // { name: 'Joseph', age: 26 }
const updated = { ...user, age: 27 };       // { name: 'Joseph', age: 27 }
// 後面的屬性會覆蓋前面的 ↑

// 合併兩個物件
const extra = { city: '高雄', role: 'admin' };
const full = { ...user, ...extra };
// { name: 'Joseph', age: 26, city: '高雄', role: 'admin' }

為什麼 React 這麼常用展開運算子?

React 要求狀態更新必須用全新的物件或陣列,不能直接修改(mutate)原本的。 這是因為 React 判斷狀態有沒有改變,是比對「參考(reference)」而非「內容」。 展開運算子剛好可以輕鬆建立新物件,同時保留原本的屬性。

state-update.jsx
const [user, setUser] = useState({ name: 'Joseph', age: 26 });

// ❌ 錯誤:直接修改原物件
// React 看到的還是「同一個物件的記憶體位址」→ 不會重新渲染
user.age = 27;
setUser(user);

// ✅ 正確:用展開運算子建立新物件
// React 看到「不同的物件記憶體位址」→ 觸發重新渲染
setUser({ ...user, age: 27 });

// 陣列也是一樣的概念
const [items, setItems] = useState(['蘋果', '香蕉']);

// ❌ 錯誤:直接修改原陣列
items.push('芒果');
setItems(items);

// ✅ 正確:建立新陣列
setItems([...items, '芒果']);

TIP

展開 vs 解構——很多人會搞混:
const copy = [...arr] 是展開(建立新陣列)
const [first, ...rest] = arr 是解構(取出元素),...rest 這裡叫「Rest Parameter」
形狀一樣,但用途不同——看左邊是賦值目標還是值表達式來判斷。
4

模板字串 `${ }`

模板字串用反引號`,鍵盤左上角 Tab 鍵上方)包住,在字串裡用 ${} 嵌入變數或運算式。比喻:就像填空題——先把空格留好,需要的時候再填進去。

template-literals.js
const name = 'Joseph';
const age = 26;

// 舊方法(字串拼接):容易出錯,又難讀
const greeting = '你好,' + name + '!你今年 ' + age + ' 歲。';

// 模板字串:直覺多了
const greeting = `你好,${name}!你今年 ${age} 歲。`;
// 你好,Joseph!你今年 26 歲。

// ${}  裡面可以放任何 JavaScript 運算式
const price = 100;
const qty = 3;
console.log(`總計:${price * qty}`);   // 總計:300 元
console.log(`明年:${age + 1}`);        // 明年:27 歲

// 多行字串(舊方法要 \n,模板字串直接換行)
const message = `
  親愛的 ${name}  您的訂單已確認。
  總計:NT$${price * qty}
`;

// 在 React 裡:動態 className
const isActive = true;
const className = `btn ${isActive ? 'btn-active' : 'btn-default'}`;
// 'btn btn-active'

REACT

在 React / Next.js 裡最常見的用法:動態 CSS className。 比如 Tailwind 的類名組合:
`px-4 py-2 ${isActive ? 'bg-blue-500' : 'bg-gray-200'}`
不過更複雜的情況下,大家通常改用 clsxcn 工具函式。
5

import / export——模組化的基礎

每個 .js / .tsx 檔案就是一個「模組」。模組可以把裡面的東西「輸出」(export),讓其他模組「輸入」(import)使用。比喻:就像公司的各個部門——財務部把報表「輸出」給管理部,管理部「輸入」後才能做決策。

math.js(輸出)
// Named Export:可以有多個,用名字區分
export function add(a, b) {
  return a + b;
}

export function multiply(a, b) {
  return a * b;
}

export const PI = 3.14159;

// Default Export:每個檔案只能有一個 default
export default function divide(a, b) {
  return a / b;
}
main.js(輸入)
// Named Import:用大括號,名字要對應
import { add, multiply, PI } from './math.js';

// Default Import:沒有大括號,可以取任何名字
import divide from './math.js';
import myDivide from './math.js';  // 名字隨便取,都對應那個 default

// 兩種一起 import
import divide, { add, multiply } from './math.js';

// 重新命名(避免名字衝突)
import { add as plus } from './math.js';

// 全部 named export 放到一個物件
import * as MathUtils from './math.js';
MathUtils.add(1, 2);  // 3

React 程式碼裡到處都是 import

MyComponent.tsx(React 真實範例)
// React 核心:useState 和 useEffect 是 named export
import React, { useState, useEffect } from 'react';

// UI 元件庫
import { Card, Button, Chip } from '@heroui/react';

// Next.js 提供的 Link Component(default export)
import Link from 'next/link';

// 動畫函式庫
import { motion } from 'framer-motion';

// 你自己寫的 Component(default export)
import CodeBlock from '@/components/blog/CodeBlock';

// 你自己寫的工具函式(named export)
import { formatDate, cn } from '@/lib/utils';

INFO

Named export vs Default export——什麼時候用哪個?
一個約定俗成的規則:Component 用 default export(因為一個檔案通常只有一個主要 Component),工具函式用 named export(一個 utils 檔案可能輸出很多函式)。不過這只是慣例,不是強制規定。
6

選用鏈 ?. 與空值合併 ??

這兩個符號是防禦性編程的好工具——在資料可能是 nullundefined 的情況下,避免程式噴錯炸掉。 在串接 API 資料、處理 React props 時特別常用。

optional-chaining.js
// 問題:直接存取 null 物件的屬性會噴錯
const user = null;
console.log(user.name);  // ❌ TypeError: Cannot read properties of null

// 舊方法(很囉嗦)
const name = user && user.address && user.address.city;

// 選用鏈 ?.:如果 ? 前面是 null/undefined,直接回傳 undefined,不噴錯
console.log(user?.name);                  // undefined(不噴錯)
console.log(user?.address?.city);         // undefined(巢狀也安全)
console.log(user?.getAge?.());            // undefined(函式呼叫也能用)

// 空值合併 ??:左邊是 null 或 undefined 時,回傳右邊的預設值
const displayName = user?.name ?? '訪客'; // '訪客'
const age = user?.age ?? 0;              // 0

// 注意 ?? 和 || 的差異:
// || 把 0、''、false 也視為假值
// ?? 只把 null 和 undefined 視為「沒有值」
const count = 0;
console.log(count || 10);  // 10(因為 0 是 falsy)
console.log(count ?? 10);  // 0(0 不是 null/undefined,所以用 0)

React Component 的防禦性寫法

Profile.jsx
// 從 API 取得的資料,一開始可能是 null/undefined
function Profile({ user }) {
  return (
    <div>
      {/* ?. 和 ?? 讓你安全讀取巢狀屬性 */}
      <h1>{user?.displayName ?? '未知用戶'}</h1>
      <p>{user?.email ?? '未設定 Email'}</p>
      <p>{user?.profile?.bio ?? '這個人很神秘,什麼都沒留下。'}</p>

      {/* 選用鏈 + && 的組合:只有有值才顯示 */}
      {user?.isAdmin && <span>管理員</span>}

      {/* 選用鏈呼叫方法 */}
      <p>加入時間:{user?.getJoinDate?.() ?? '不詳'}</p>
    </div>
  );
}

// 使用時就算 user 是 undefined 也不會噴錯
<Profile user={undefined} />  // ✅ 不炸

綜合範例——改寫前 vs 改寫後

讓我們把同一個 Component 用 ES5 和 ES6+ 各寫一次,看看差距有多大。 邏輯完全一樣,但可讀性天差地遠:

改寫前 — ES5 傳統寫法
javascript
function BookCard(props) {
  var title = props.title;
  var author = props.author;
  var price = props.price;
  var inStock = props.inStock;

  if (inStock === undefined) {
    inStock = true;  // 手動設預設值
  }

  return (
    '<div>' +
    title + ' by ' + author +
    ' — NT$' + price +
    (inStock ? ' (現貨)' : ' (缺貨)') +
    '</div>'
  );
}
↓ ES6+ 改寫後
改寫後 — ES6+ 現代寫法
jsx
// 箭頭函式 + 解構(含預設值)+ 模板字串
const BookCard = ({ title, author, price, inStock = true }) => (
  <div>
    {`${title} by ${author} — NT$${price} ${inStock ? '(現貨)' : '(缺貨)'}`}
  </div>
);

從 13 行壓縮成 4 行,可讀性卻反而更好——這就是現代 ES6+ 語法帶來的威力。 你在 React 程式碼裡看到的「精簡」寫法,背後都是這幾個語法的組合。

六個語法速查表

箭頭函式

(a, b) => a + b

函式的速記法。React 事件處理和 .map() 回呼的標準寫法

解構賦值

const { a, b } = obj

從陣列或物件中直接取出並命名。useState、props 的核心語法

展開運算子

[...arr] {...obj}

複製並展開陣列/物件。React 狀態更新必用,確保不可變性

模板字串

`Hello ${name}`

反引號字串,可嵌入變數和運算式。動態字串的最佳寫法

import/export

import X from Y

模組系統。每個 .tsx 檔案都是模組,Component 用 default export

選用鏈 + ??

obj?.a ?? default

安全存取可能為 null/undefined 的值,給予預設值

現在你能看懂什麼了?

還記得開篇的那行程式碼嗎:

jsx
const [count, setCount] = useState(0);
//     ↑               ↑              ↑
//     陣列解構         陣列解構        函式呼叫

const UserCard = ({ name, age = 18 }) => (  // 解構 + 預設值 + 箭頭函式
  <div className={`card ${age >= 18 ? 'adult' : 'minor'}`}>  {/* 模板字串 */}
    <h2>{name ?? '訪客'}</h2>                                  {/* 空值合併 */}
  </div>
);

你現在已經能完整讀懂這段程式碼了。接下來的文章,我們會開始真正使用 React——你已經準備好了。

下一步

語法你現在全懂了。但你可能還有一個問題:React 為什麼要存在? 「傳統 HTML + JS 不是也可以做網頁嗎?」——下一篇從這個問題開始, 讓你真正理解 React 的設計哲學。

ES6
JavaScript
箭頭函式
解構賦值
展開運算子
模板字串
import/export
選用鏈
React 前置知識