首頁 web前端 js教程 深入了解JavaScript中的Symbol的使用方法_基礎知識

深入了解JavaScript中的Symbol的使用方法_基礎知識

May 16, 2016 pm 03:48 PM

Symbol 是什麼?

Symbols 不是圖標,也不是指在程式碼中可以使用小圖片:

2015728114935470.jpg (291×49)

也不是指其他一些東西的語法。那麼,Symbol 到究竟是什麼呢?
七種資料型別

JavaScript 在 1997 年被標準化時,就有 6 種資料類型,直到 ES6 出現之前,程式中的變數一定是以下 6 種資料型態之一:

    Undefined
    Null
    Boolean
    Number
    String
    Object

每種資料型態都是一系列值的組合,前面 5 種資料型態值的數量都是有限的。 Boolean 型別只有兩個值:true 和 false,當 Boolean 類型的變數賦值時,並不會產生新的值(共用了true 和 false 這兩個值)。對於 Number 和 String 來說,它們的值則多得多了,標準的說法是有 18,437,736,874,454,810,627 個 Number 類型的值(包括 NAN)。 String 類型的數量就難以統計了,我原以為是 (2144,115,188,075,855,872 ? 1) ÷ 65,535…不過也許我算錯了。

物件值的數量是無限的,每個物件都是獨一無二的,每次開啟一個網頁,都建立了一系列的物件。

ES6 中的 Symbol 也是一種資料類型,但是不是字串,也不是對象,而是一種新的資料類型:第七種資料類型。

下面我們來看一個場景,也許 Symbol 能派上用場。
一個布林值引出的問題

有時,把一些屬於其他物件的資料暫存在另一個物件中是非常方便的。例如,假設你正在編寫一個JS 庫,使用CSS 中的transition 來讓一個DOM 元素在屏幕上飛奔,你已經知道不能同時將多個transition 應用在同一個div 上,否則將使得動畫非常不美觀,你也確實有辦法來解決這個問題,但是首先你需要知道該div 是否已經在移動中。

怎麼解決這個問題呢?

其中一個方法是使用瀏覽器提供的 API 來探測元素是否處於動畫狀態,但殺雞焉用牛刀,在將元素設為移動時,你的庫就知道了該元素正在移動。

你真正需要的是一種機制來追蹤哪些元素正在移動,你可以將正在移動的元素保存在一個陣列中,每次要為一個元素設定動畫時,首先檢查一下這個元素是否已經在這個列表中。

啊哈,但是如果你的陣列非常龐大,即便是這樣的線性搜尋也會產生效能問題。

那麼,你真正想做的就是直接在元素上設定一個標誌:

if (element.isMoving) {
 smoothAnimations(element);
}
element.isMoving = true;

 
if (element.isMoving) {
 smoothAnimations(element);
}
element.isMoving = true;

登入後複製

這也有一些潛在的問題,不得不承認這樣一個事實:還有其他程式碼也可能操作該 ODM 元素。

  •     在其他程式碼中,你所建立的屬性會被 for-in 或 Object.keys() 列舉;
  •     在其他一些函式庫中也許已經使用了相同的方式(在元素上設定了相同的屬性),那麼這將和你的程式碼發生衝突,產生不可預期的結果;
  •     其他一些函式庫可能在將來會使用相同的方式,這也會與你的程式碼發生衝突;
  •     標準委員會可能會為每個元素添加一個 .isMoving() 原生方法,那麼你的程式碼就徹底不能工作了。

當然,對於最後三個問題,你可以選擇一個無意義的不會有人會使用到的字串:

if (element.__$jorendorff_animation_library$PLEASE_DO_NOT_USE_THIS_PROPERTY$isMoving__) {
 smoothAnimations(element);
}
element.__$jorendorff_animation_library$PLEASE_DO_NOT_USE_THIS_PROPERTY$isMoving__ = true;
 
if (element.__$jorendorff_animation_library$PLEASE_DO_NOT_USE_THIS_PROPERTY$isMoving__) {
 smoothAnimations(element);
}
element.__$jorendorff_animation_library$PLEASE_DO_NOT_USE_THIS_PROPERTY$isMoving__ = true;

登入後複製

這似乎太不靠譜了,看了讓人眼睛痛。

你也可以用加密演算法來產生一個幾乎唯一的字串:

// get 1024 Unicode characters of gibberish
var isMoving = SecureRandom.generateName();

...

if (element[isMoving]) {
 smoothAnimations(element);
}
element[isMoving] = true;
 
// get 1024 Unicode characters of gibberish
var isMoving = SecureRandom.generateName();
 
...
 
if (element[isMoving]) {
 smoothAnimations(element);
}
element[isMoving] = true;

登入後複製

object[name] 語法允許我們將任何字串作為屬性名,程式碼能正常運作,衝突幾乎是不可能了,程式碼看起來也美觀多了。

但是,這回導致糟糕的調試體驗,每次使用console.log() 打印出包含該屬性的元素時,你回看到一個龐大的垃圾字符串,並且如果還不止一個這樣的屬性呢?每次刷新後屬性名都發生了變化,怎麼樣讓這些屬性看起來更直覺呢?

為什麼這麼難?我們只是為了保存一個小小的標誌位。
用 Symbol 來解決問題

Symbol 值可以由程式創建,並可以作為屬性名,而且不用擔心屬性名衝突。

var mySymbol = Symbol();

var mySymbol = Symbol();

登入後複製

调用 Symbol() 方法将创建一个新的 Symbol 类型的值,并且该值不与其它任何值相等。

与数字和字符串一样,Symbol 类型的值也可以作为对象的属性名,正是由于它不与任何其它值相等,对应的属性也不会发生冲突:

obj[mySymbol] = "ok!"; // guaranteed not to collide
console.log(obj[mySymbol]); // ok!
 
obj[mySymbol] = "ok!"; // guaranteed not to collide
console.log(obj[mySymbol]); // ok!
登入後複製

下面是使用 Symbol 来解决上面的问题:

// create a unique symbol
var isMoving = Symbol("isMoving");

...

if (element[isMoving]) {
 smoothAnimations(element);
}
element[isMoving] = true;
 
// create a unique symbol
var isMoving = Symbol("isMoving");
 
...
 
if (element[isMoving]) {
 smoothAnimations(element);
}
element[isMoving] = true;

登入後複製

上面代码需要注意几点:

  • 方法 Symbol("isMoving") 中的 "isMoving" 字符串被称为 Symbol 的描述信息,这对调试非常有帮助。可以通过 console.log(isMoving) 打印出来,或通过 isMoving.toString() 将 isMoving 转换为字符串时,或在一些错误信息中显示出来。
  • element[isMoving] 访问的是 symbol-keyed 属性,除了属性名是 Symbol 类型的值之外,与其它属性都一样。
  • 和数组一样,symbol-keyed 属性不能通过 . 操作符来访问,必须使用方括号的方式。
  • 操作 symbol-keyed 属性也非常方便,通过上面代码我们已经知道如何获取和设置 element[isMoving] 的值,我们还可以这样使用:if (isMoving in element) 或 delete element[isMoving]。
  • 另一方面,只有在 isMoving 的作用域范围内才可以使用上述代码,这可以实现弱封装机制:在一个模块内创建一些 Symbol,只有在该模块内部的对象才能使用,而不用担心与其它模块的代码发生冲突。

由于 Symbol 的设计初衷是为了避免冲突,当遍历 JavaScript 对象时,并不会枚举到以 Symbol 作为建的属性,比如,for-in 循环只会遍历到以字符串作为键的属性,Object.keys(obj)和 Object.getOwnPropertyNames(obj) 也一样,但这并不意味着 Symbol 为键的属性是不可枚举的:使用 Object.getOwnPropertySymbols(obj) 这个新方法可以枚举出来,还有 Reflect.ownKeys(obj) 这个新方法可以返回对象中所有字符串和 Symbol 键。(我将在后面的文章中详细介绍 Reflect 这个新特性。)

库和框架的设计者将会发现很多 Symbol 的用途,稍后我们将看到,JavaScript 语言本身也对其有广泛的应用。
Symbol 究竟是什么呢

> typeof Symbol()
"symbol"
 
> typeof Symbol()
"symbol"

登入後複製

Symbol 是完全不一样的东西。一旦创建后就不可更改,不能对它们设置属性(如果在严格模式下尝试这样做,你将得到一个 TypeError)。它们可以作为属性名,这时它们和字符串的属性名没有什么区别。

另一方面,每个 Symbol 都是独一无二的,不与其它 Symbol 重复(即便是使用相同的 Symbol 描述创建),创建一个 Symbol 就跟创建一个对象一样方便。

ES6 中的 Symbol 与传统语言(如 Lisp 和 Ruby)中的 Symbol 中的类似,但并不是完全照搬到 JavaScript 中。在 Lisp 中,所有标识符都是 Symbol;在 JavaScript 中,标识符和大多数属性仍然是字符串,Symbol 只是提供了一个额外的选择。

值得注意的是:与其它类型不同的是,Symbol 不能自动被转换为字符串,当尝试将一个 Symbol 强制转换为字符串时,将返回一个 TypeError。

> var sym = Symbol("<3");
> "your symbol is " + sym
// TypeError: can't convert symbol to string
> `your symbol is ${sym}`
// TypeError: can't convert symbol to string
 
> var sym = Symbol("<3");
> "your symbol is " + sym
// TypeError: can't convert symbol to string
> `your symbol is ${sym}`
// TypeError: can't convert symbol to string

登入後複製

應該避免這樣的強制轉換,應該使用 String(sym) 或 sym.toString() 來轉換。
取得 Symbol 的三種方法

  1.     Symbol() 每次呼叫時都會傳回一個唯一的 Symbol。
  2.     Symbol.for(string) 從 Symbol 註冊表中傳回對應的 Symbol,與上個方法不同的是,Symbol 註冊表中的 Symbol 是共用的。也就是說,如果你呼叫 Symbol.for("cat") 三次,都會傳回相同的 Symbol。當不同頁面或同一頁面不同模組需要共用 Symbol 時,註冊表就非常有用。
  3.     Symbol.iterator 傳回語言預先定義的某些 Symbol,每個都有其特殊的用途。

如果你仍不確定 Symbol 是否有用,那麼接下來的內容將會非常有趣,因為我將為你示範 Symbol 的實際應用程式。
Symbol 在 ES6 規範中的應用

我們已經知道可以使用 Symbol 來避免程式碼衝突。之前在介紹iterator 時,我們也解析了for (var item of myArray) 內部是以呼叫myArray[Symbol.iterator]() 開始的,當時我提到這個方法可以使用myArray.iterator() 來代替,但使用Symbol 的後向相容性更好。

在 ES6 中還有一些地方使用到了 Symbol。 (這些特性還沒有在 FireFox 中實現。)

  •     使 instanceof 可擴展。在 ES6 中,object instanceof constructor 表達式被標準化為建構子的一個方法:constructor[Symbol.hasInstance](object),這意味著它是可擴展的。
  •     消除新功能與舊程式碼之間的衝突。
  •     支援新類型的字串符合。在 ES5 中,當呼叫 str.match(myObject) 時,首先會嘗試將 myObject 轉換為 RegExp 物件。在 ES6 中,首先將檢查 myObject 中是否有 myObject[Symbol.match](str) 方法,在所有正規表示式工作的地方都可以提供一個自訂的字串解析方法。

這些用途還比較窄,但僅僅透過我文章中的程式碼很難看到這些新特性產生的重大影響。 JavaScript 的 Symbol 是 PHP 和 Python 中 __doubleUnderscores 的改進版本,標準組織將使用它來為語言添加新特性,而不會對已有程式碼產生影響。

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1664
14
CakePHP 教程
1421
52
Laravel 教程
1315
25
PHP教程
1266
29
C# 教程
1239
24
神秘的JavaScript:它的作用以及為什麼重要 神秘的JavaScript:它的作用以及為什麼重要 Apr 09, 2025 am 12:07 AM

JavaScript是現代Web開發的基石,它的主要功能包括事件驅動編程、動態內容生成和異步編程。 1)事件驅動編程允許網頁根據用戶操作動態變化。 2)動態內容生成使得頁面內容可以根據條件調整。 3)異步編程確保用戶界面不被阻塞。 JavaScript廣泛應用於網頁交互、單頁面應用和服務器端開發,極大地提升了用戶體驗和跨平台開發的靈活性。

JavaScript的演變:當前的趨勢和未來前景 JavaScript的演變:當前的趨勢和未來前景 Apr 10, 2025 am 09:33 AM

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

JavaScript引擎:比較實施 JavaScript引擎:比較實施 Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

Python vs. JavaScript:學習曲線和易用性 Python vs. JavaScript:學習曲線和易用性 Apr 16, 2025 am 12:12 AM

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

JavaScript:探索網絡語言的多功能性 JavaScript:探索網絡語言的多功能性 Apr 11, 2025 am 12:01 AM

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

如何使用Next.js(前端集成)構建多租戶SaaS應用程序 如何使用Next.js(前端集成)構建多租戶SaaS應用程序 Apr 11, 2025 am 08:22 AM

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

從C/C到JavaScript:所有工作方式 從C/C到JavaScript:所有工作方式 Apr 14, 2025 am 12:05 AM

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

使用Next.js(後端集成)構建多租戶SaaS應用程序 使用Next.js(後端集成)構建多租戶SaaS應用程序 Apr 11, 2025 am 08:23 AM

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

See all articles