博客 / 詳情

返回

探索JavaScript的秘密令牌:獨一無二的`Symbol`數據類型

🧑‍💻 寫在開頭

點贊 + 收藏 === 學會🤣🤣🤣

引言

在JavaScript的廣闊世界中,數據類型構成了其最基礎的語法元素。隨着ES6的發佈,這個大家庭迎來了兩位新成員:BigIntSymbol。如果説BigInt是為了解決大數運算的精度問題,那麼Symbol的誕生,則像是一把為對象屬性開啓“隱私空間”和“唯一命名”的神奇鑰匙。本文將帶你深入理解這個“獨一無二”的簡單數據類型。

一、認識Symbol:一種新的簡單數據類型

JavaScript的八種數據類型,是每一位開發者的基本功,常被戲稱為“七上八下”:

  • 簡單數據類型 (7種)

    • 傳統numberbooleanstringnullundefined
    • ES6新增bigintsymbol
  • 複雜數據類型 (1種)object

Symbol雖然用起來有點像構造函數Symbol()),但它本質上是簡單數據類型。你可以通過typeof操作符來驗證這一點。

// 1.js
const id1 = Symbol();
console.log(typeof id1); // 輸出:symbol

二、Symbol的核心特性:絕對的獨一無二

Symbol最核心、最迷人的特性,就是它的“獨一無二性”。每次調用Symbol()函數,都會返回一個全新的、與其他任何Symbol都不同的值,即使它們擁有相同的描述(label)。

// 1.js
const id1 = Symbol();
const id2 = Symbol();
console.log(id1 === id2); // 輸出:false

// 2.js
const s1 = Symbol('二哈');
const s2 = Symbol('二哈');
console.log(s1 === s2); // 輸出:false

你可以為Symbol傳入一個可選的字符串參數作為描述(label) ,例如Symbol('descrption')。這個描述僅僅是為了調試時方便識別,它不會影響Symbol的唯一性。兩個描述相同的Symbol,依然是兩個完全不同的值。這就像給兩把不同的鎖都貼上了“書房”的標籤,但鎖的齒紋(值)完全不同。

三、Symbol的核心應用:作為對象屬性的唯一鍵

Symbol最主要、最實用的場景,就是作為對象的屬性鍵(key) 。在ES6之前,對象的鍵只能是字符串,這在一個複雜、多人協作的代碼庫中極易引發命名衝突。

JavaScript是動態語言,任何人都可以輕鬆修改對象的屬性。當項目代碼龐大時,你可能會無意中覆蓋掉他人定義的重要屬性,或者自己的屬性被他人覆蓋,造成難以排查的Bug。

Symbol的引入,就是為了解決這個問題。用Symbol作為屬性名,可以創造出絕對安全的、不會與任何字符串屬性或其他Symbol屬性衝突的私有屬性

1. 如何定義Symbol屬性?

你需要使用計算屬性名的語法,在[]中寫入Symbol變量。

// 2.js
const secretKey = Symbol('secret'); // 創建一個Symbol
console.log(secretKey, '//////'); // Symbol(secret) //////

const a = 'ecut';
const user = {
    [secretKey]: '111222', // 使用Symbol作為鍵
    email: '123456@qq.com',
    name: '張三',
    'a': '456', // 字符串'a'作為鍵
    [a]: '123'  // 使用變量a的值`'ecut'`作為鍵,相當於 `ecut: '123'`
};
console.log(user.ecut, user[a]); // 輸出:123 123

2. Symbol屬性的獨特優勢

  • 命名安全secretKey這個屬性是獨一無二的,全局任何地方都無法用[Symbol('secret')]以外的其他Symbol訪問到它,也無法用字符串'secretKey'來訪問,這避免了屬性被意外覆蓋。
  • 標籤不影響唯一性:即使兩個Symbol描述相同,它們作為鍵也是互不衝突的。
// 3.html
const classRoom = {
    [Symbol('Mark')]: {grade: 50, gender: 'male'},
    [Symbol('oliva')]: {grade: 80, gender: 'female'},
    // 即使標籤(描述)和上面一樣,這也是一個新的、獨立的屬性
    [Symbol('oliva')]: {grade: 85, gender: 'female'}, 
    "dl": ["張三","李四"]
};

上述代碼中,第二個[Symbol('oliva')]並沒有覆蓋第一個,而是創建了一個全新的屬性,完美解決了同名標籤可能帶來的衝突。

3. 枚舉與遍歷:Symbol的“隱藏”特性

Symbol屬性還有一個重要特性:它們不會被常規的遍歷方法枚舉到。例如,for...in循環、Object.keys()Object.values()Object.entries()以及JSON.stringify()都會“忽略”Symbol屬性。

// 3.html
for (const person in classRoom) {
    console.log(classRoom[person], '////'); // 只會打印出 "dl" 的值
}

這使得Symbol屬性具備了一定的“私有”和“內置”屬性特徵,不會被輕易暴露出去。

如果你需要獲取對象中所有的Symbol屬性,必須使用專門的方法:

// 3.html
const syms = Object.getOwnPropertySymbols(classRoom); // 返回一個包含對象自身所有Symbol鍵的數組
console.log(syms); // 打印出 [Symbol(Mark), Symbol(oliva), Symbol(oliva)]

// 可以結合map方法獲取這些屬性的值
const data = syms.map(sym => classRoom[sym]);
console.log(data); // 打印出三個學生的對象數組

四、總結

Symbol是ES6為解決JavaScript長期存在的屬性命名衝突和元編程問題而引入的一種優雅方案。它:

  1. 是簡單數據類型,獨一無二。
  2. 是創建對象唯一鍵的理想選擇,尤其在多人協作和庫的開發中,能有效保證屬性安全。
  3. 具有“半隱藏”特性,不會被常規方法枚舉,需用Object.getOwnPropertySymbols()獲取。

掌握了Symbol,你就擁有了在JavaScript對象中創建“命名空間”和“內部插槽”的能力,讓你的代碼結構更清晰、更健壯。

如果對您有所幫助,歡迎您點個關注,我會定時更新技術文檔,大家一起討論學習,一起進步。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.