iT邦幫忙

2026 iThome 鐵人賽

DAY 24
0
JavaScript

30 天新世代 JavaScript 自我學習指南系列 第 24 篇

Day 24|關於深拷貝這件事:structuredClone、序列化與資料邊界

  • 分享至 

  • xImage
  •  

摘要

深拷貝不只是一層層往下複製。structuredClone() 先把資料序列化成一份記錄,再重建出全新的資料;它保留 Date、Map 等型別與資料內部的引用關係,但 function、class 方法這類不是資料的東西帶不過去。

  • 前置知識:理解 object reference 與淺拷貝;讀過 Day 23 的 toReversed()/with() 更好;用過 JSON.stringify()/JSON.parse() 即可。

  • 學習路線:Day 23–25 都在談 copy,一篇比一篇往下一層。Day 23 只換了外層 Array;今天把 copy 推進到整份資料,並看清哪些東西複製不過去。

  • 標籤:structuredClone Deep Copy Serialization JSON DataCloneError

https://ithelp.ithome.com.tw/upload/images/20260927/20145251QDPxgXqeQi.png


今日學習目標

  1. 用 structuredClone() 建立一份可以放心修改的編輯草稿。
  2. 說明序列化與反序列化在做什麼,以及為什麼 JSON 適合傳輸、structuredClone() 適合複製。
  3. 理解共用引用與循環引用在複製時如何被保留。
  4. 判斷哪些值能複製。
  5. 判斷什麼時候該用 structuredClone(),什麼時候該沿修改路徑做淺拷貝。

延續 Day 23 的使用者列表。列表已經用 toSorted() 排好,現在使用者點進其中一位,開啟「編輯個人資料」表單。

使用者最後可能按下「取消」,所以編輯過程不能直接改到原始資料。我們需要先建立一份草稿,讓表單上的修改只發生在草稿裡。

但 Day 23 介紹的 copy methods 只會換掉最外層:

const copied = users.toReversed();

copied === users;       // false:外層 Array 是新的
copied[0] === users[1]; // true:裡面的 user 仍是同一個 object

copied 和 users 雖然是不同的 Array,裡面的 user object 卻仍然共用同一個 reference。假如表單修改的是 user object:

copied[0].name = 'May';

users[1].name; // 'May'

原始的 users 也會跟著改變。更深一層的 settings 當然也有同樣的問題。

編輯草稿需要的不是只換掉外層的副本,而是一份連內部資料都彼此獨立的副本,這種複製通常稱為「深拷貝」。

而 JavaScript 已經提供了一個直接完成這件事的工具:

structuredClone(value);

一、structuredClone() 建立編輯草稿

把使用者點開的那一位複製成草稿:

const user = {
  name: 'Rafael',
  settings: {
    theme: 'dark',
    fontSize: 16,
  },
};

const draft = structuredClone(user);

draft === user;                   // false:外層是新的
draft.settings === user.settings; // false:裡面的 settings 也是新的

接著在草稿上任意修改:

draft.name = 'May';
draft.settings.theme = 'light';

user.name;           // 'Rafael'
user.settings.theme; // 'dark'

表單上的修改都只發生在 draft 裡。按下「取消」,直接丟掉 draft 就好,user 從頭到尾沒被動過;按下「儲存」,再用 Day 23 的 with() 把草稿放回列表,產生下一個版本:

const nextUsers = users.with(index, draft);

不過在 structuredClone() 出現之前,JavaScript 開發者常用這段程式做深拷貝:

const copy = JSON.parse(JSON.stringify(value));

既然兩種寫法看起來都能複製 object,為什麼還需要 structuredClone()?答案不只是哪一個支援的型別比較多,而是它們從一開始就在完成兩種不同的工作。


二、序列化與反序列化:JSON 和 Structured Clone 的共同原理

以前串 API 時,拿到的資料要先 parse 才能用;要把物件送上網路,又得先轉成字串。我一直把這兩步當成必經流程,知道要做,卻不太理解它們各自在做什麼、又差在哪裡。

其實這兩步就是反序列化與序列化。弄懂它們,也就能看懂 JSON 和 structuredClone() 的差別。

拆開帶走,再組回來

以第一節的 user 來說,外層 object 的 settings 存的是一個 reference,也就是一條指向另一個 object 的箭頭:

user
 └─► { name: 'Rafael', settings: ● }
                                 │
                                 ▼
                     { theme: 'dark', fontSize: 16 }

這條箭頭只在目前的執行環境裡有意義。要把資料送上網路、存進檔案或交給 Web Worker,就得先把它攤平成一串可以帶走的內容,這叫序列化(serialize)。箭頭的處理方式很直接:不寫箭頭,改寫箭頭指向的內容。

{"name":"Rafael","settings":{"theme":"dark","fontSize":16}}

拿到這串內容,照著重新建立 object,叫反序列化(deserialize)。每讀到一個 { 就建立一個新 object,再重新接上箭頭。舊的箭頭從來沒被帶過去,所以結果自然是深拷貝。

可以想成搬家:把家具拆開裝箱、附上說明書,到新家再照說明書組回去。組回來的是新家具,而說明書能寫什麼,決定了能組回什麼。

至於 toString() 這類方法,不必寫進說明書。它們放在所有 object 共用的原型 Object.prototype 上,反序列化只要把新 object 接上環境內建的原型,就能借用:

const parsed = JSON.parse('{"name":"Rafael"}');

Object.hasOwn(parsed, 'toString'); // false:不在自己身上
typeof parsed.toString;            // 'function':從原型借來的

規格裡,JSON.parse() 就是把文字當成 object literal 來執行,得到的 object 和直接寫 { name: 'Rafael' } 一樣。就像螺絲起子不必跟著家具寄,新家本來就有;但新家只有內建的工具,自訂 class 的方法就沒有了,第五節會再看到。


JSON:各種語言都看得懂的文字說明書

JSON.stringify() 是序列化,結果是一段字串;JSON.parse() 是反序列化。JSON 的寫法借自 JavaScript,但它是獨立的文字格式,Python、Go、Java 都能讀寫,所以 API 和設定檔大多用它。

為了讓每種語言都看得懂,JSON 能寫的東西刻意很少:

JSON 能寫的 寫出來的樣子
object {"name":"Rafael"}
array ["js","css"]
string "Rafael"
number 42
boolean true、false
null null

沒有 Date、Map、undefined,也寫不出「這裡和前面那個是同一個 object」。遇到這些值,JSON.stringify() 只能換掉、略過或丟出錯誤;JSON.parse() 讀回來時,已經不知道原本是什麼。


Structured Clone:只給 JavaScript 看的說明書

structuredClone() 同樣先序列化、再反序列化,但說明書是寫給另一個 JavaScript 環境看的,例如同一個頁面或 Web Worker:

原本的 JavaScript 資料圖
        │ StructuredSerialize     序列化成記錄
        ▼
與執行環境無關的序列化記錄
        │ StructuredDeserialize   從記錄反序列化
        ▼
新的 JavaScript 資料圖

中間這份說明書,規範裡稱為序列化記錄。它不是文字,也不會交到我們手上。既然讀的一方也懂 JavaScript,它能寫得更細:

  • JavaScript 的型別:Date、Map、Set、BigInt、undefined 等。
  • object 之間的連線:「這裡和前面那個是同一個 object」,第四節會看到。

JSON 保留的是能寫進 JSON 文字的內容;Structured Clone 重建的是資料內容,以及資料之間的連線關係。


三、JSON 為傳輸而生,structuredClone() 為複製而生

兩者都會序列化、反序列化,但出發點不同:

JSON.stringify()/JSON.parse() structuredClone()
設計來做什麼 把資料變成文字,送給伺服器、其他語言或寫進檔案 在 JavaScript 環境裡建立一份副本
說明書給誰看 任何語言 另一個 JavaScript 環境

所以 JSON.parse(JSON.stringify(value)) 其實是借用一個傳輸格式來做複製:資料先被翻譯成各種語言都懂的文字,再翻譯回來。翻譯時,JSON 寫不出來的東西就會被換掉、略過或直接報錯。

下面三個例子,都是這個「翻譯損失」。

Date 變成字串

const source = {
  createdAt: new Date('2026-09-01T08:00:00Z'),
};

const jsonCopy = JSON.parse(JSON.stringify(source));
const clone = structuredClone(source);

console.log(jsonCopy.createdAt instanceof Date);
// false

console.log(clone.createdAt instanceof Date);
// true

console.log(typeof(jsonCopy.createdAt)) // string
console.log(typeof(clone.createdAt)) // object

JSON 只留下日期的文字表示;structuredClone() 則保留 Date 這個值的型別與時間值。


Map 不是 JSON object

const source = {
  scores: new Map([
    ['Rafael', 90],
    ['Mina', 95],
  ]),
};

const jsonCopy = JSON.parse(JSON.stringify(source));
const clone = structuredClone(source);

console.log(jsonCopy.scores);
// {}

console.log(clone.scores instanceof Map);
// true

console.log(clone.scores.get('Mina'));
// 95

為什麼 JSON 會得到空的 {}?

JSON.stringify() 處理 object 時,只會把它自己身上的可列舉 property 寫成 "key": value。可是 Map 的 entries 不是 property,而是存在 Map 內部、只能透過 get()、set() 或迭代存取的資料:

const scores = new Map([['Rafael', 90]]);

Object.keys(scores);
// []:身上沒有任何 property

JSON.stringify(scores);
// '{}'

JSON 看不到 entries,自然寫不出來。Set 也一樣,會變成 {}。

{}、[] 這種直接寫在程式碼裡的值叫字面量(literal),JSON 的格式就是從這種寫法來的。Map、Set 沒有字面量,只能用 new Map()、new Set() 建立,因為它們本來就不是「另一種寫 {} 或 [] 的方式」,而是有自己的規則:Map 的 key 可以是任何值,Set 會自動排除重複。

JSON 沒有描述這些規則的寫法,所以真的要用 JSON 傳 Map,得自己先轉成 JSON 寫得出來的形式,例如 Object.fromEntries(scores) 或 [...scores],收到後再轉回來。


undefined 與 BigInt 也反映出格式邊界

const source = {
  note: undefined,
  amount: 10n,
};

const clone = structuredClone(source);

console.log('note' in clone);
// true

console.log(clone.amount);
// 10n

structuredClone() 能保留這兩個 JavaScript 值,JSON 則各有各的問題。

undefined:JSON 能寫的值裡沒有 undefined,所以 object 裡值為 undefined 的 property 會直接被略過,JSON.stringify({ note: undefined }) 得到的是 '{}'。

BigInt:平常比較少碰到,先認識一下。

JavaScript 一般的數字是 Number,但它能精確表示的整數有上限,也就是 Number.MAX_SAFE_INTEGER(9007199254740991)。超過之後,數字可能會悄悄變成相鄰的另一個:

9007199254740993;
// 9007199254740992

BigInt 就是用來表示任意大整數的型別,寫法是在數字後面加上 n:

9007199254740993n;
// 9007199254740993n:精確保留

實務上最常遇到的,是資料庫的 64 位元 ID,或以最小單位計算的金額這類超出安全範圍的大整數。

那 JSON 為什麼不乾脆把 10n 寫成 10?因為讀回來時,JSON.parse() 會把數字一律變成 Number:

JSON.parse('{"id":9007199254740993}').id;
// 9007199254740992

如果 JSON.stringify() 默默把 BigInt 寫成數字,讀回來就變成 Number:型別不對,大數字還會失去精確度,而且不會有任何錯誤提示。所以規格選擇直接丟出 TypeError,寧可一開始就失敗,也不要悄悄把資料改錯:

JSON.stringify({ amount: 10n });
// TypeError: BigInt value can't be serialized in JSON

要用 JSON 傳大整數,常見做法是先轉成字串,收到後再轉回 BigInt:

const text = JSON.stringify({ id: 9007199254740993n.toString() });
// '{"id":"9007199254740993"}'

BigInt(JSON.parse(text).id);
// 9007199254740993n

這不代表 JSON 設計得不好。JSON 的優點正是格式簡單、跨語言而且適合變成文字;只是「JavaScript 能表示的所有值」本來就不是 JSON 的目標。

先整理目前看到的差異

問題 JSON.parse(JSON.stringify(value)) structuredClone(value)
設計目的 傳輸:轉成 JSON 文字後再解析 複製:建立可序列化 JavaScript 資料的副本
產物是否有中間文字 有 API 不回傳中間文字
Date 變成字串 保留 Date
Map、Set 不保留 entries 的原型別 保留型別與內容
undefined object property 會消失 保留
BigInt 丟出 TypeError 保留
適合寫進檔案或送給只懂 JSON 的 API 是 否,它不是文字格式

一句話記:要把資料送出去,用 JSON;要在程式裡複製一份,用 structuredClone()。

到這裡只比較了「資料內容」。下一個問題更深入:如果兩個 property 原本指向同一個 object,複製後還會是同一個嗎?


四、深拷貝不只是需要複製很多層,還要處理 reference

我原本以為,深拷貝 API 要做的事很單純:遇到 object 就剝開一層,把裡面的值一個個複製下去,直到最底層為止。

但實際想下去,要考慮的事情不少🤠:

  • 巢狀的 object 要一路複製到底,不能只換外層。
  • 兩個地方原本指向同一個 object,複製完還要是同一個嗎?
  • 如果資料會繞回自己,一層層剝下去,會不會永遠剝不完?

這一節就照這個順序,一層一層看下去。

第一層:巢狀物件為什麼會互相影響?

先看最常見的展開運算子:

const profile = {
  name: 'Rafael',
  settings: {
    theme: 'dark',
  },
};

const copiedProfile = { ...profile };

console.log(copiedProfile === profile);
// false

console.log(copiedProfile.settings === profile.settings);
// true

spread 建立了新的外層 object,但兩邊的 settings 仍指向同一個 object:

profile ────────┐
                 ├──► settings object
copiedProfile ──┘

所以修改 copiedProfile.settings.theme,原資料也會跟著變。這就是淺拷貝的邊界。

structuredClone(profile) 則會建立新的巢狀 settings:

const clone = structuredClone(profile);

console.log(clone.settings === profile.settings);
// false

但如果只把深拷貝理解成「遇到巢狀 object 就繼續往下複製」,下一個案例就會出問題。


第二層:同一個 object 被引用兩次,不能複製成兩份

const settings = { theme: 'dark' };

const source = {
  desktop: settings,
  mobile: settings,
};

在來源裡,desktop 與 mobile 指向同一個 object:

console.log(source.desktop === source.mobile);
// true

如果只是每遇到一個 property 就遞迴複製,settings 可能被複製兩次,原本我們習慣的物件「共用設定」的意思就消失了,兩次的物件變成獨立個體了,即便原本資料指向語意是同一份。

JSON 文字沒有 reference 可以表達這個關係:

const jsonCopy = JSON.parse(JSON.stringify(source));

console.log(jsonCopy.desktop === jsonCopy.mobile);
// false

Structured Clone 會保留原本的連線關係:

const clone = structuredClone(source);

console.log(clone.desktop === clone.mobile);
// true

console.log(clone.desktop === settings);
// false

它建立了一個新的 settings,但 clone 裡的兩條路仍共同指向那個新 object。要注意,它保留的是資料內部彼此的連線,不是和原資料的連線:

哪一種連線 JSON Structured Clone
副本和原資料之間 切斷 切斷
資料內部「兩處指向同一個 object」 消失,變成兩份 保留,指向同一個新 object

這份記錄只在 JavaScript 環境之間傳遞,例如同一個頁面、Web Worker 或 IndexedDB,不會變成 JSON 走網路。從 API 拿到的 JSON,這層關係早在對方序列化成 JSON 文字時就消失了,收到後也找不回來。


為什麼要這樣設計?

HTML Standard 在 StructuredSerializeInternal 開頭就寫明了目的:同一個 object 不要序列化兩次。這個規則一次帶來三個好處。

一、副本的行為和原本一致。 在 source 裡,desktop 和 mobile 共用同一份設定,改一邊,另一邊也會變。「是不是同一個」本身就是資料的意義。如果複製成兩份,副本看起來一樣,改起來的行為卻不同:

clone.desktop.theme = 'light';
clone.mobile.theme;    // 'light':和原資料一樣,仍是共用設定

jsonCopy.desktop.theme = 'light';
jsonCopy.mobile.theme; // 'dark':共用關係不見了

二、循環引用不會無限遞迴。 如果每遇到一個 object 就往下複製,碰到「走著走著又指回自己」的資料會永遠停不下來。記住看過哪些 object,才能正常結束。下一個進階補充會看到這個例子。

三、資料量不會爆炸。 共用的 object 每次都複製一份,大小可能呈指數成長:

let node = { value: 1 };

for (let i = 0; i < 20; i++) {
  node = { left: node, right: node };
}

每一層的 left、right 都指向同一個下一層。structuredClone(node) 只會建立 21 個 object;JSON.stringify(node) 卻會把共用的部分展開 2²⁰ 次,產生約 3000 萬個字元的文字。


進階補充:循環 reference 也能被重建

一般表單、API response 與前端 state,通常不需要刻意設計成循環引用。它不容易轉成 JSON,也會讓遞迴處理與除錯變複雜。

這裡介紹它,不是鼓勵日常資料這樣設計,而是用一個極端案例看出:JSON 處理的是可寫成文字的資料,Structured Clone 處理的是 object graph。

const node = { name: 'root' };
node.self = node;

const clone = structuredClone(node);

console.log(clone.self === clone);
// true

JSON 無法用一般文字表示「走著走著又指回自己」,所以:

JSON.stringify(node);
// TypeError: Converting circular structure to JSON

Structured Clone 則能重建這條回到自己的連線。知道這個結果即可;多數應用不需要主動建立循環資料。


進階補充:規範怎麼做到?靠的是 memory 記錄

HTML Standard 的 Structured Clone Algorithm 在序列化時會維護一份 memory:

已經看過的來源 object
        ↓ 對應
它的序列化記錄

第一次遇到某個 object 時,演算法把對應記錄放進 memory;再次遇到同一個 object,就重用那筆記錄,而不是複製第二次。以 source 為例,概念上像這樣:

#1 Object { desktop: → #2, mobile: → #2 }
#2 Object { theme: 'dark' }

這只是示意,實際記錄的格式由瀏覽器自己決定。

反序列化時也有另一份對應的 memory:

序列化記錄
        ↓ 對應
已經建立的新 object

因此重複引用會指向同一個新 object,循環引用也能接回正在建立的 object。這是規範要求的結果,但使用 API 時不需要自己管理這份 memory。

可以用 stack/heap 幫助理解,但要知道它是簡化模型

常見的入門說法會把變數與 object 畫成:

stack                         heap
┌────────────────┐           ┌─────────────────────┐
│ source         │──────────►│ 外層 object          │
└────────────────┘           │ desktop ─┐          │
                             │ mobile ──┴─► settings│
                             └─────────────────────┘

clone 之後,可以想成另一組新的 object:

stack                         heap
┌────────────────┐           ┌─────────────────────┐
│ clone          │──────────►│ 新的外層 object      │
└────────────────┘           │ desktop ─┐          │
                             │ mobile ──┴─► 新設定  │
                             └─────────────────────┘

這張圖想表達的是:clone 不再指向來源 object,但 clone 內部原有的共享關係仍然存在。

不過要加上一個重要但不難懂的註記:

「變數放 stack、object 放 heap」是協助理解 reference 的簡化心智模型,不是 ECMAScript 或 Structured Clone 規範要求的實際記憶體配置。

JavaScript engine 可以用不同方式最佳化記憶體。規範保證的是我們能觀察到的結果:哪些值相等、哪些 reference 相同,以及循環關係是否保留。

這裡可以把整段濃縮成一句話:

JSON 解析後得到的是符合文字內容的新資料 tree;Structured Clone 重建的是一份新的 object graph。


五、哪些值能複製?哪些不能?

structuredClone() 會先把資料寫成一份序列化記錄,再照記錄重建。就像搬家的說明書,一個值能不能複製,取決於它能不能寫進這份記錄。

先用實務上的分法,而不是硬背長清單。

類型 structuredClone() 重點
primitive(含 undefined、BigInt) 可以 不需要共享 reference
Array、plain object 可以 遞迴重建內容
Date、RegExp 可以 保留這類內建值的資料
Map、Set 可以 entries 也會遞迴處理
ArrayBuffer、TypedArray 可以 預設是複製;可選擇 transfer
function、Symbol 不可以 會丟 DataCloneError
DOM node 不可以 會丟 DataCloneError;DOM 不是一般資料快照
自訂 class instance 不要假設可保留 class 行為 要特別檢查 prototype/方法需求

最常踩到的是 function:

const user = {
  name: 'Rafael',
  greet() {
    return `Hi, ${this.name}`;
  },
};

structuredClone(user);
// DOMException: DataCloneError

這很合理。function 不只是資料;它還帶有可執行程式碼、閉包與所在環境。Structured Clone 的目標是安全地搬運或快照資料,不是複製執行中的程式。

可以把這條界線記成:

能被拿去傳遞、儲存、重建的資料,通常適合 structured clone;程式行為、DOM 與執行環境,通常不適合。


class instance 為什麼要特別小心?

假設我們有:

class Cart {
  constructor(items) {
    this.items = items;
  }

  total() {
    return this.items.reduce((sum, item) => sum + item.price, 0);
  }
}

const cart = new Cart([
  { name: 'Book', price: 300 },
]);

const clone = structuredClone(cart);

console.log(clone instanceof Cart);
// false

資料還在,方法卻不見了:

clone.items;
// [{ name: 'Book', price: 300 }]

clone.total;
// undefined

回想第二節:反序列化不會複製方法,只會把新 object 接到環境內建的原型上。而序列化記錄能寫的型別都是內建的,例如 Object、Array、Date、Map,沒有一種能寫「這是一個 Cart」。

所以 cart 只會被記成一般的 Object,組回來接上的是 Object.prototype;total() 所在的 Cart.prototype 沒有被記下來,自然接不回去:

Object.getPrototypeOf(clone) === Object.prototype;
// true

就像新家有內建的螺絲起子,卻沒有你自己訂做的那把工具。

因此 structuredClone() 適合用在 data object;若領域模型依賴 class 的 methods、private fields 或 prototype,通常應由 class 自己提供明確的序列化/重建方式。


六、structuredClone() 不是 state update 的預設解

看到深拷貝這麼好用後....容易產生那我需要更新資料把整份資料複製就行?:

const nextState = structuredClone(state);
nextState.user.settings.theme = 'light';

它的確可行,但通常不應是每次更新 state 的第一選擇。Day 23 比較三種更新做法時,這種「整份深拷貝再改」就是被當成反例的那一種,原因有三個:

  1. 它會處理整份資料圖,即使只有一個很深的欄位改變。
  2. 每個可複製的巢狀 object 都是新 reference,依 reference 判斷哪些區塊改變的工具無法重用未變更分支。
  3. 它無法處理 function、DOM 或 class 行為;若 state 已經混入這些值,問題不是「少了一個 clone」,而是資料模型需要整理。

這和第一節的編輯草稿並不矛盾。

草稿要的是一份和原資料完全切開的副本,改了、丟了都不影響原資料;state update 要的則是下一個版本,應該和舊版本共用沒改的部分。同一個工具,用在需要隔離的地方是對的,用在每次更新就是用錯了地方。

當更新路徑清楚時,還是優先寫出有意圖的淺拷貝:

const nextState = {
  ...state,
  user: {
    ...state.user,
    settings: {
      ...state.user.settings,
      theme: 'light',
    },
  },
};

這段確實比較長,但它精準表達:只有 user.settings.theme 這條路徑改變。

若資料結構太深以至於這樣寫難以維護,下一步通常是調整資料 shape、使用能處理 immutable update 的工具,或把需要隔離的資料在邊界處 clone;不是不加思考地把每次更新都換成 structuredClone()。


今日總結

回到開頭的編輯草稿:我們需要一份連內部都獨立的副本,structuredClone() 做到了,但它做的不只是「一層層剝開複製」。今天的重點可以收成四句:

  • 深拷貝是先序列化、再反序列化;說明書能寫什麼,決定了能組回什麼。
  • JSON 為傳輸而生,structuredClone() 為複製而生:要把資料送出去用 JSON,要在程式裡複製一份用 structuredClone()。
  • Structured Clone 會保留資料內部的共用與循環引用,但和原資料之間完全切開。
  • 不能複製的值有兩種結果:丟出 DataCloneError,或不報錯卻悄悄遺失,例如 class 的方法。

和 Day 23 放在一起看,每種做法保證的 copy 範圍都不同:

需求 做法 新的範圍
排序、反轉、替換 Array 的結果 toSorted()、toReversed()、with() 外層 Array
只改某個已知的巢狀欄位 沿更新路徑做 spread 修改路徑上的 object
需要一份完全獨立的副本或快照 structuredClone() 整份可序列化資料
要保留 class 方法、function、DOM reference 自己設計資料格式與重建流程 不要依賴 structuredClone()

這張表背後只有一個問題:

這份資料接下來是要被「更新」,還是需要「隔離」?

理解「copy 到哪一層」,也就是程式實際要做的意圖,往往比記住任何 API 名稱更重要...。


參考資料


上一篇
Day 23|Array 明明只是排序,為什麼原始資料被改掉了?
下一篇
Day 25|Uint8Array 不是 Array:從 ArrayBuffer 看 bytes 的共用、複製與移交
系列文
30 天新世代 JavaScript 自我學習指南 共 25 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言