Logo

新人日誌

首頁關於我部落格

新人日誌

Logo

網站會不定期發佈技術筆記、職場心得相關的內容,歡迎關注本站!

網站
首頁關於我部落格
部落格
分類系列文

© 新人日誌. All rights reserved. 2020-present.

JavaScript 資料是怎麼儲存的?深入了解 Value Type 與 Reference Type

最後更新:2025年4月19日JavaScript

你可能遇過這種狀況:

const a = [1, 2, 3];
const b = [1, 2, 3];
console.log(a === b); // false

看起來陣列內容一模一樣,為什麼卻不相等?

又或者:

const user = { name: "Amy" };
const users = [user];
user.name = "Bob";
console.log(users[0].name); // "Bob"

你沒有動到 users 陣列,但裡面的值卻被改變了?

這一切的背後,跟 JavaScript 如何儲存「原始資料」和「物件資料」有關,也就是:值類型(Value Type)與參考類型(Reference Type)。


資料類型大分類

JavaScript 將資料分成兩大類,每一類的儲存與處理方式都不一樣:

值類型(Value Types)— 存放「值」本身

包含:

  • string
  • number
  • boolean
  • null
  • undefined
  • symbol
  • bigint

這些值是「簡單且不可變」的,變數儲存的就是資料本體。

const a = 100;
const b = 100;
console.log(a === b); // true,因為內容一樣

參考類型(Reference Types)— 存放「指向記憶體位置的參考」

包含:

  • object
  • array
  • function

這些資料結構比較複雜,變數儲存的是「記憶體位置(Reference)」,也就是:資料本體其實存在別的地方。

const obj1 = { id: 1 };
const obj2 = { id: 1 };
console.log(obj1 === obj2); // false,即使內容看起來一樣

儲存邏輯差在哪?記憶體觀念圖解

類型儲存在變數中的內容比較時行為是否共享資料
值類型值的本體比較的是值❌ 各自獨立
參考類型記憶體參考(指標)比較的是記憶體位置✅ 指向同一資料
儲存在變數中的內容值的本體
比較時行為比較的是值
是否共享資料❌ 各自獨立
儲存在變數中的內容記憶體參考(指標)
比較時行為比較的是記憶體位置
是否共享資料✅ 指向同一資料

📌 這就是為什麼:

[1, 2] === [1, 2]; // false,因為不同記憶體位置
flowchart LR
    subgraph 程式碼
    A["let age = 30;"]
    B["let age1 = age;"]
    C["age = 31;"]
    D["let obj = { name: 'Tom' };"]
    E["let obj2 = obj;"]
    F["obj2.name = 'Jerry';"]
    end
    
    subgraph 記憶體儲存格
    subgraph 值類型儲存區 Stack
    M1[("00XX1<br><i>記憶體位置編號</i>")] --- V1["30<br><i>儲存值</i>"]
    M2[("00XX2<br><i>記憶體位置編號</i>")] --- V2["31<br><i>儲存值</i>"]
    M3[("00XX3<br><i>記憶體位置編號</i>")] --- V3["30<br><i>儲存值</i>"]
    end
    
    subgraph 參考類型儲存區 Heap
    M4[("00YY1<br><i>變數記憶體位置</i>")] --- V4["FF01<br><i>儲存參考位址</i>"]
    M5[("00YY2<br><i>變數記憶體位置</i>")] --- V5["FF01<br><i>儲存參考位址</i>"]
    
    M6[("FF01<br><i>物件實際記憶體位置</i>")] --- V6["{name: 'Jerry'}<br><i>物件實際內容</i>"]
    end
    end
    
    A -->|"建立變數"| M1
    B -->|"複製值"| M3
    C -->|"修改值"| M2
    D -->|"建立物件參考"| M4
    E -->|"複製參考"| M5
    
    V4 -->|"指向實際物件"| M6
    V5 -->|"指向同一個物件"| M6
    
    
    style M1 fill:#ddf8dd,stroke:#000000
    style M2 fill:#ddf8dd,stroke:#000000
    style M3 fill:#ddf8dd,stroke:#000000
    style M4 fill:#fff0f0,stroke:#000000
    style M5 fill:#fff0f0,stroke:#000000
    style M6 fill:#fff0f0,stroke:#000000
    
    style V1 fill:#ddf8dd,stroke:#000000
    style V2 fill:#ddf8dd,stroke:#000000
    style V3 fill:#ddf8dd,stroke:#000000
    style V4 fill:#fff0f0,stroke:#000000,color:#008800
    style V5 fill:#fff0f0,stroke:#000000,color:#008800
    style V6 fill:#fff0f0,stroke:#000000



常見應用情境:比較、修改、複製資料的正確觀念

這一章節,我們會從實務常見的幾種操作出發,說明「值類型」和「參考類型」在 JavaScript 中的實際行為差異。

比較值 vs. 比較參考:看的是值,還是地址?

🧾 值類型的比較 —— 內容一致就相等

console.log(5 === 5); // true
console.log("hello" === "hello"); // true
  • 值類型(例如 number、string)的比較是「看資料本身是否相等」。
  • 兩邊的內容一樣,就會被視為相等。

🧠 想像成比對兩張紙條,兩張紙都寫著 “5”,你當然會說它們一樣。

🧾 參考類型的比較 —— 看的是記憶體位置

console.log([1, 2, 3] === [1, 2, 3]); // false
console.log({ name: "Tom" } === { name: "Tom" }); // false
  • 即使資料內容「一模一樣」,JavaScript 也會判斷為 不同。
  • 因為它們是兩個「不同記憶體位置」的資料。

🧠 就像兩份一模一樣的簡報檔案,但儲存在不同的 USB 隨身碟裡——你拿來比較時,是比「是哪個隨身碟」,不是「簡報長什麼樣」。

修改值 vs. 修改參考:哪裡在變?

🧾 修改值類型:變數之間彼此獨立

let scoreA = 80;
let scoreB = 80;

scoreA = 100;

console.log(scoreA); // 100
console.log(scoreB); // 80 ✅ 不受影響

🧠 比喻:就像你給了兩個人各自的紙本成績單

A 把自己的擦掉重寫成 100,B 的那份完全沒改變。

🧾 修改參考類型:共用資料,同步變動

const student = { name: "Mary" };
const studentA = student;
const studentB = student;

studentA.name = "John";

console.log(studentB.name); // "John" ❗️也變了

🧠 比喻:你把同一個 Google 文件連結給 A 和 B

A 把名字改了,B 一打開,也看到已經改成「John」的版本。


如何正確「複製」參考類型?

當你不希望物件之間互相影響(例如修改 A 不要影響 B),就必須「複製資料內容」而不是「複製參考」。

展開運算子(Shallow Copy)—— 適用於物件扁平結構

const original = { name: "David" };
const copy = { ...original };

copy.name = "Ethan";

console.log(original.name); // "David"
  • 使用 { ...物件 } 語法,可以複製第一層屬性的資料。
  • 適用於「只有一層」的物件。
  • 修改 copy 不會影響到 original,彼此獨立。

🧠 就像你影印了一張紙表單出來,你可以在影印本上改名字,不會動到原件。

展開運算子的限制:無法處理巢狀資料

const original = { info: { score: 100 } };
const copy = { ...original };

copy.info.score = 50;

console.log(original.info.score); // 50 ❗️會一起變
  • 雖然第一層 info 是複製的,但 info 的內容(score)還是指向同一記憶體位址。
  • 這種情況下,兩者還是共享同一份「內部資料」。

深層複製(Deep Copy)—— 解決巢狀同步問題

const obj = { info: { score: 100 } };
const deepCopy = JSON.parse(JSON.stringify(obj));

deepCopy.info.score = 50;

console.log(obj.info.score); // 100 ✅ 不受影響
  • 使用 JSON.parse(JSON.stringify(...)) 可以將物件轉成字串再重建,達到深層複製效果。
  • 適用於資料結構中沒有函式、undefined、Symbol、Date 等特殊值時。

🧠 像是把一棟房子的所有東西都照著蓋一棟新的——包含家具、樓層結構、每一個房間的內容都重建一次。

延伸技巧:進階使用者可用工具函式庫

如果你需要更彈性、保留特殊資料型別(例如 Date、Map、Set)的深層複製,推薦使用:

  • lodash.cloneDeep()
  • structuredClone()(現代瀏覽器內建)

生活情境重新比喻:值 vs. 參考,就像「影本 vs. 雲端文件」

值類型(Value Type)就像「影印文件」

你請兩個同事來拿履歷資料:

const resumeA = "我是一份影印履歷";
const resumeB = "我是一份影印履歷";
  • A 和 B 拿到的內容一模一樣,但彼此是兩張紙。
  • A 把自己的紙塗鴉,不會影響 B 的那張。
  • 系統比較這兩張時(===),看的是內容,只要一樣就算相等。

🔍 這就是值類型:變數儲存的是「資料本身」,獨立、純粹、可比。

參考類型(Reference Type)就像「共用 Google 文件的連結」

你不是列印紙,而是給兩人同一個 Google 文件的連結:

const doc = { content: "我是一份雲端履歷" };
const personA = doc;
const personB = doc;
  • 他們連到同一份文件,改任何地方,另一人也會看到。
  • 系統比較這兩個變數(===),會是 true,因為是同一個記憶體位置。

🔍 這是參考類型:變數存的是「資料的地址」,而不是資料本身。

❗️ 但如果你開了兩個長得一樣的 Google 文件(不同網址)

const docA = { content: "我是一份雲端履歷" };
const docB = { content: "我是一份雲端履歷" };

console.log(docA === docB); // false
  • 即使他們的內容一模一樣,但網址不同,屬於兩份獨立文件。
  • 改了其中一份,另一份不會變。
  • 系統判斷時會說:這是兩個不同的物件(記憶體位置不同)。

🧠 換句話說:「你可以有兩份內容完全一樣的文件,但它們還是兩個不同的檔案。」

整體整理:值 vs. 參考的生活對照表

概念值類型(Value)參考類型(Reference)
類比紙本履歷(影印件)雲端文件(Google Docs)
儲存方式存放資料本身存放文件網址(記憶體位置)
比較依據比對履歷內容比對是否是「同一份文件」(同一個網址)
修改行為改一份不影響另一份改了就會同步變動(共用同一份)
相同內容時視為相等(如果值一樣)還是視為不同(如果不是同一份物件)
避免同步方式每人發一份影印本建立一個新 Google 文件,再傳新連結給另一人
值類型(Value)紙本履歷(影印件)
參考類型(Reference)雲端文件(Google Docs)
值類型(Value)存放資料本身
參考類型(Reference)存放文件網址(記憶體位置)
值類型(Value)比對履歷內容
參考類型(Reference)比對是否是「同一份文件」(同一個網址)
值類型(Value)改一份不影響另一份
參考類型(Reference)改了就會同步變動(共用同一份)
值類型(Value)視為相等(如果值一樣)
參考類型(Reference)還是視為不同(如果不是同一份物件)
值類型(Value)每人發一份影印本
參考類型(Reference)建立一個新 Google 文件,再傳新連結給另一人

為什麼學這個很重要?

當你進一步學習 React、Vue 或寫 API、管理狀態時,如果搞不清楚資料是值還是參考,可能會:

  • 不小心改到不該動的資料(例如原本的 state)
  • 寫出「看不見變動來源」的 bug
  • 比較資料時一直得到 false,以為值不一樣

只要記住這個簡單口訣:

🔑 原始類型看內容,物件類型看記憶體位置。

總結與對照表

特性值類型參考類型
儲存方式直接儲存資料本體儲存記憶體參考(指標)
比較方式比較資料內容比較是否為同一個記憶體位置
複製後是否連動不會預設會連動
範例10, "hi", true{}, [], function(){}
值類型直接儲存資料本體
參考類型儲存記憶體參考(指標)
值類型比較資料內容
參考類型比較是否為同一個記憶體位置
值類型不會
參考類型預設會連動
值類型10, "hi", true
參考類型{}, [], function(){}

目前還沒有留言,成為第一個留言的人吧!

發表留言

留言將在審核後顯示。

JavaScript

目錄

  • 資料類型大分類
  • 值類型(Value Types)— 存放「值」本身
  • 參考類型(Reference Types)— 存放「指向記憶體位置的參考」
  • 儲存邏輯差在哪?記憶體觀念圖解
  • 常見應用情境:比較、修改、複製資料的正確觀念
  • 比較值 vs. 比較參考:看的是值,還是地址?
  • 修改值 vs. 修改參考:哪裡在變?
  • 如何正確「複製」參考類型?
  • 展開運算子(Shallow Copy)—— 適用於物件扁平結構
  • 展開運算子的限制:無法處理巢狀資料
  • 深層複製(Deep Copy)—— 解決巢狀同步問題
  • 延伸技巧:進階使用者可用工具函式庫
  • 生活情境重新比喻:值 vs. 參考,就像「影本 vs. 雲端文件」
  • 值類型(Value Type)就像「影印文件」
  • 參考類型(Reference Type)就像「共用 Google 文件的連結」
  • 整體整理:值 vs. 參考的生活對照表
  • 為什麼學這個很重要?
  • 總結與對照表