ES2015 引入了一些長期以來開發者期待的語言特性,但也有一些鮮為人知的功能,其優勢並不明顯,例如 Symbol。
Symbol 是一種新的原始數據類型,是一個獨特的標記,保證不會與其他 Symbol 衝突。從這個意義上說,您可以將 Symbol 視為一種 UUID(通用唯一標識符)。讓我們看看 Symbol 的工作原理以及我們可以用它做什麼。
要點
創建新的 Symbol
創建新的 Symbol 非常簡單,只需調用 Symbol 函數即可。請注意,這只是一個標準函數,而不是對象構造函數。嘗試使用 new 運算符調用它會導致 TypeError。每次調用 Symbol 函數時,您都會獲得一個新的且完全唯一的值。
<code class="language-javascript">const foo = Symbol(); const bar = Symbol(); foo === bar // false</code>
也可以通過將字符串作為第一個參數傳遞來創建帶標籤的 Symbol。標籤不會影響 Symbol 的值,但對於調試很有用,如果調用 Symbol 的 toString() 方法,則會顯示該標籤。可以使用相同的標籤創建多個 Symbol,但這沒有任何優勢,這可能會導致混淆。
<code class="language-javascript">let foo = Symbol('baz'); let bar = Symbol('baz'); foo === bar // false // console.log(foo); // Symbol(baz)</code>
Symbol 的用途
Symbol 可以很好地替代字符串或整數作為類/模塊常量:
<code class="language-javascript">class Application { constructor(mode) { switch (mode) { case Application.DEV: // 设置开发环境的应用程序 break; case Application.PROD: // 设置生产环境的应用程序 break; default: throw new Error('无效的应用程序模式:' + mode); } } } Application.DEV = Symbol('dev'); Application.PROD = Symbol('prod'); // 使用示例 const app = new Application(Application.DEV);</code>
字符串和整數不是唯一的值;例如,數字 2 或字符串“development”也可能在程序的其他地方用於不同的目的。使用 Symbol 可以讓我們對提供的值更有信心。
Symbol 的另一個有趣的用途是作為對象屬性鍵。如果您曾經將 JavaScript 對像用作哈希映射(用 PHP 術語來說是關聯數組,用 Python 術語來說是字典),那麼您將熟悉使用方括號表示法獲取/設置屬性:
<code class="language-javascript">const foo = Symbol(); const bar = Symbol(); foo === bar // false</code>
使用方括號表示法,我們也可以使用 Symbol 作為屬性鍵。這樣做有幾個優點。首先,您可以確定基於 Symbol 的鍵永遠不會衝突,不像字符串鍵那樣可能與對象現有屬性或方法的鍵衝突。其次,它們不會在 for...in 循環中枚舉,並且會被 Object.keys()、Object.getOwnPropertyNames() 和 JSON.stringify() 等函數忽略。這使得它們非常適合您不希望在序列化對象時包含的屬性。
<code class="language-javascript">let foo = Symbol('baz'); let bar = Symbol('baz'); foo === bar // false // console.log(foo); // Symbol(baz)</code>
但是,值得注意的是,使用 Symbol 作為鍵並不能保證隱私。提供了一些新工具來允許您訪問基於 Symbol 的屬性鍵。 Object.getOwnPropertySymbols() 返回任何基於 Symbol 的鍵的數組,而 Reflect.ownKeys() 將返回所有鍵的數組,包括 Symbol。
<code class="language-javascript">class Application { constructor(mode) { switch (mode) { case Application.DEV: // 设置开发环境的应用程序 break; case Application.PROD: // 设置生产环境的应用程序 break; default: throw new Error('无效的应用程序模式:' + mode); } } } Application.DEV = Symbol('dev'); Application.PROD = Symbol('prod'); // 使用示例 const app = new Application(Application.DEV);</code>
公共 Symbol
由於 Symbol 鍵屬性對於 ES6 之前的代碼實際上是不可見的,因此它們非常適合在不破壞向後兼容性的情況下向 JavaScript 的現有類型添加新功能。所謂的“公共”Symbol 是 Symbol 函數的預定義屬性,用於自定義某些語言特性的行為,並用於實現諸如迭代器之類的新的功能。
Symbol.iterator 是一個公共 Symbol,用於向對象分配一個特殊方法,允許對其進行迭代:
<code class="language-javascript">const data = []; data['name'] = 'Ted Mosby'; data['nickname'] = 'Teddy Westside'; data['city'] = 'New York';</code>
內置類型String、Array、TypedArray、Map 和Set 都有一個默認的Symbol.iterator 方法,當在for...of 循環中或使用擴展運算符時使用這些類型的實例時,會調用該方法。瀏覽器也開始使用 Symbol.iterator 鍵來允許以相同的方式迭代 DOM 結構(例如 NodeList 和 HTMLCollection)。
全局註冊表
規範還定義了一個運行時範圍的 Symbol 註冊表,這意味著您可以在不同的執行上下文(例如文檔和嵌入式 iframe 或 Service Worker 之間)存儲和檢索 Symbol。
Symbol.for(key) 從註冊表中檢索給定鍵的 Symbol。如果鍵不存在 Symbol,則返回一個新的 Symbol。正如您可能預期的那樣,對相同鍵的後續調用將返回相同的 Symbol。
Symbol.keyFor(symbol) 允許您檢索給定 Symbol 的鍵。使用註冊表中不存在的 Symbol 調用該方法將返回 undefined:
<code class="language-javascript">const user = {}; const email = Symbol(); user.name = 'Fred'; user.age = 30; user[email] = 'fred@example.com'; Object.keys(user); // ['name', 'age'] Object.getOwnPropertyNames(user); // ['name', 'age'] JSON.stringify(user); // {"name":"Fred","age":30}</code>
用例
在一些用例中,使用 Symbol 可以提供優勢。前面文章中已經提到過的一個用例是,當您想要向對象添加不會在對象序列化時包含的“隱藏”屬性時。
庫作者還可以使用 Symbol 安全地為客戶端對象添加屬性或方法,而無需擔心覆蓋現有鍵(或其鍵被其他代碼覆蓋)。例如,小部件組件(例如日期選擇器)通常使用各種選項和需要存儲在某處狀態進行初始化。將小部件實例分配給 DOM 元素對象的屬性並不是理想的,因為該屬性可能會與另一個鍵衝突。使用基於 Symbol 的鍵可以巧妙地規避此問題,並確保不會覆蓋您的 Widget 實例。有關此想法的更詳細探討,請參閱 Mozilla Hacks 博客文章 ES6 in Depth: Symbols。
瀏覽器支持
如果您想試驗 Symbol,主流瀏覽器的支持相當好。如您所見,Chrome、Firefox、Microsoft Edge 和 Opera 的當前版本都原生支持 Symbol 類型,移動設備上的 Android 5.1 和 iOS 9 也支持。如果您需要支持舊版瀏覽器,也可以使用 polyfill。
結論
雖然引入 Symbol 的主要原因似乎是為了方便在不破壞現有代碼的情況下向語言添加新功能,但它們確實有一些有趣的用途。所有開發人員至少都應該對它們有基本的了解,並且熟悉最常用的公共 Symbol 及其用途。
關於 ES6 Symbol 的常見問題解答
ES6 Symbol 是 ES6(ECMAScript 6)中引入的一種新的原始數據類型。它們是唯一且不可變的數據類型,可用作對象屬性的標識符。它們可用於為對象創建私有屬性,實現元級編程概念以及避免命名衝突。它們還可用於為對象定義自定義迭代行為。
您可以使用 Symbol() 函數創建新的 Symbol。此函數每次調用時都會返回一個唯一的 Symbol,即使您向其傳遞相同的參數也是如此。例如,let symbol1 = Symbol('symbol'); let symbol2 = Symbol('symbol');
此處,symbol1 和 symbol2 是不同的 Symbol,即使向 Symbol() 函數傳遞了相同的參數“symbol”。
您可以使用方括號中的 Symbol 作為對象鍵。例如,let symbol = Symbol('key'); let obj = {}; obj[symbol] = 'value';
此處,Symbol“symbol”用作對象“obj”中的鍵。
全局 Symbol 註冊表是一個共享環境,您可以在其中創建、訪問和共享跨不同領域(如 iframe 或 Service Worker)的 Symbol。您可以使用 Symbol.for(key) 在全局註冊表中創建 Symbol,並使用 Symbol.keyFor(symbol) 訪問它。
您可以將 Symbol 與對像一起使用以創建唯一鍵。這對於避免命名衝突很有用,因為 Symbol 始終是唯一的。例如,let symbol = Symbol('key'); let obj = { [symbol]: 'value' };
此處,Symbol“symbol”用作對象“obj”中的鍵。
是的,您可以通過在其上調用 toString() 方法將 Symbol 轉換為字符串。這將返回格式為“Symbol(description)”的字符串,其中“description”是您傳遞給 Symbol() 函數的可選描述。
是的,您可以將 Symbol 與數組一起使用。例如,您可以使用 Symbol 作為唯一屬性鍵來存儲有關數組的元數據。但是,Symbol 不包含在數組的 length 屬性中,並且大多數數組方法都不會返回它們。
是的,您可以將 Symbol 與函數一起使用。例如,您可以使用 Symbol 作為唯一屬性鍵來存儲有關函數的元數據。但是,Symbol 不包含在函數的 length 屬性中,並且大多數函數方法都不會返回它們。
是的,您可以將 Symbol 與類一起使用。例如,您可以使用 Symbol 作為唯一屬性鍵來存儲有關類的元數據。但是,Symbol 不包含在類的 length 屬性中,並且大多數類方法都不會返回它們。
是的,您可以將 Symbol 與字符串一起使用。例如,您可以使用 Symbol 作為唯一屬性鍵來存儲有關字符串的元數據。但是,Symbol 不包含在字符串的 length 屬性中,並且大多數字符串方法都不會返回它們。
以上是ES6行動:符號及其用途的詳細內容。更多資訊請關注PHP中文網其他相關文章!