給前端修練家的筆記:… 語法

… <= 這個東西不管是在聊天室看到,還是在 Javascript / TypeScript 裡面看到都讓人一頭霧水

這篇要來簡單說明一下當你在前端的程式中看到 … 這代表什麼意思

首先,根據不同的出現場合,它可能代表兩種東西,就像是你朋友小王出現在你家床上,跟出現在熱炒店門口是不同功能。

這篇介紹 … 相關的兩種用法 Spread Syntax、Rest Syntax。


Spread Syntax:把資料展開

Spread Syntax 的作用,就是把原本包在一起的資料展開。

至於「展開」什麼? 這個根據它出現的位置不同而有差異。

  1. 在函式呼叫或陣列中:此時 … 展開的是 iterable 。例如陣列、字串
    const arr = [1, 2, 3]; console.log(...arr); // 等同於 console.log(1, 2, 3)
  2. 在物件中:此時 … 展開的是 物件的可列舉屬性,像這樣
    const obj = { a: 1, b: 2 }; const copy = { ...obj }; //把 obj 裡的屬性展開到新的物件中

Spread Syntax的用法,常常用來複製、合併資料,或是在保留原本大部分內容的情況下,只更新其中一部分。

我自己會把重點放在「把 key-value 複製一份出來」這個特性。

來看一個常見情境:切換主題模式(light / dark)。

const settings = {
  theme: "light",
  fontSize: 14
};

let currentSettings = settings;

document.getElementById("toggleBtn").onclick = function () {
  currentSettings = changeMode(currentSettings);
  console.log("更新後:", currentSettings);
};

function changeMode(prevSettings) {
  return {
    ...prevSettings,
    theme: prevSettings.theme === "light" ? "dark" : "light"
  };
}

請把重點放在 changeMode 裡的這一行:

...prevSettings

在這裡,它的作用是把 prevSettings 這個物件中的屬性展開到新的物件裡。

假設 prevSettings 是:

{
  theme: "light",
  fontSize: 14
}

那麼:

{                               {
  ...prevSettings      =>          theme: "light",
                                   fontSize: 14
}                               }

然後,程式又多寫了一次 theme

theme: prevSettings.theme === "light" ? "dark" : "light"

這表示我們要用新的 theme 覆蓋原本展開出來的 theme

所以最後回傳的結果會是:

{
  theme: "dark",
  fontSize: 14
}

保留原本的大部分屬性,只覆蓋需要更新的欄位。

如果你還是有點混亂,那可以只記得這個就好

「把原本包在一起的資料(像是陣列或者物件)展開,方便複製、合併或傳遞。」

Rest Syntax:把剩下的資料收集起來

我個人會把 Spread Syntax 和這邊要講的 Rest Syntax 分開理解,建議不熟悉的讀者將這兩個概念分開來理解,對自己會比較有幫助。

那麼 Rest Syntax是什麼呢?

如果說 Spread 是把資料展開,那 Rest 就是把資料收集起來,透過 Rest Syntax 把「剩下的值」收集成一個陣列(或物件)。

使用時重點會放在「收集剩餘資料」。

Rest syntax 常見的出現場景主要有三種:

1. 函式參數(rest parameters)

「不確定有幾個參數」時使用。

function fn(a, ...rest) {
    console.log(a);   // 第一個參數
    console.log(rest);// 剩下的全部(陣列)
}

常見於:

  • 參數數量不固定(例如 log、event handler)
  • 想把多餘參數集中處理

2. 陣列解構(array destructuring)

const [first, ...rest] = [1, 2, 3, 4];
// first = 1
// rest = [2, 3, 4]

常見於:

  • 取頭幾個元素
  • 做資料拆分(例如 pagination、queue)
  1. 物件解構(object destructuring)
const { a, ...rest } = { a: 1, b: 2, c: 3 };
// a = 1
// rest = { b: 2, c: 3 }

常見於:

  • 移除某些欄位(例如過濾敏感資料)
  • 拆分設定物件

有發現嗎?不論是哪一種出現場景,核心概念都一樣

就是「把剩下的資料收集起來」。

這邊補充一個重要的特性,就是Rest Syntax 使用上「必須放在最後一個位置」。

那麼一樣提供一個簡單例子:記錄操作紀錄。

function logAction(action, ...details) {
    console.log("操作:", action);
    console.log("細節:", details);
}

logAction("LOGIN");
logAction("DELETE_POST", 12345, "admin");

這時候執行結果:

logAction("LOGIN")
// action = "LOGIN"
// details = []

logAction("DELETE_POST", 12345, "admin")
// action = "DELETE_POST"
// details = [12345, "admin"]

... 出現在函式參數列表中,這個例子是函式參數的使用方式(也就是常見的Rest parameters)

在這個例子裡,固定要接的第一個參數是 action

除了 action 以外,後面多傳進來的東西,都會被收進 details 裡。

總結

本篇介紹了 … 所代表的兩種語法:

  • Spread(展開) → 把資料「攤開」
  • Rest(收集) → 把資料「收起來」

References:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters#syntax

https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/Destructuring


留言

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *