詳解JavaScript的Symbol類型、隱藏屬性、全局註冊表
Symbol類型的使用
在前文《JavaScript的8種數據類型》中,我們已經簡單的介紹過瞭JavaScript
的Symbol
類型,下面對其使用方法和使用場景做一個簡單的介紹。
Symbol簡介
Symbol
類型是JavaScript
中的一種特殊的類型,特殊在所有的Symbol
類型值都互不相同。我們可以使用“Symbol”來表示唯一的值,下面是創建Symbol
對象的案例:
let id = Symbol();
這樣我們就創建瞭一個Symbol
類型的值,並把這個值存儲在瞭變量id
中。
Symbol類型的描述
我們在創建一個Symbol
類型變量的時候,可以在參數中傳入一些秒屬性的字符串,用於描述這個變量的用途信息。
例如:
let id1 = Symbol('狂拽酷炫吊炸天的小明的id'); let id2 = Symbol('低調奢華有內涵的婷婷的id');
Symbol
類型在任何時候都是不同的,即使他們擁有相同的描述信息,描述隻是一個標簽,除此之外就沒有別的用途瞭,例如:
let id1 = Symbol('id'); let id2 = Symbol('id'); console.log(id1==id2);//false
這個標簽存在的意義,個人認為和Symbol
不能直觀的看到內部具體值的特性有關,通過添加一個描述信息,讓我們對變量的用途有更直觀的瞭解。
Symbol不會隱式轉字符串
JavaScript
中的大多數類型都可以直接轉換成字符串類型輸出,所以我們不能直觀的看到它的值到底是什麼,例如我們可以直接用alert(123)
把數字123
轉換成字符串彈出。
但是Symbol
類型比較特殊,它不能直接轉換,例如:
let id = Symbol(); alert(id);//報錯,不能把Symbol類型轉為字符串
JavaScript
中的Symbol
類型不能轉成字符串是由於其內在的防治語言混亂的“語言保護”機制,因為字符串和Symbol
在本質上有著區別,不應該將其中一個轉換成另一個。
試想一下,如果Symbol
可以轉為字符串,那麼它就變成瞭一個生成獨一無二字符串的函數,就不再具備獨立數據類型的必要。
如果我們真的想知道Symbol
變量的值,我們可以使用.toString()
方法,如下所示:
let id = Symbol('this is identification'); console.log(id.toString());//Symbol(this is identification);
或者使用.description
屬性,獲取描述信息:
let id = Symbol('加油,奧利給'); console.log(id.description);//加油,奧利給”
Symbol類似作為對象的屬性鍵
根據JavaScript
的規范,隻有兩種類型的值可以作為對象的屬性鍵:
- 字符串
- Symbol
如果使用其他類型,則會隱式的轉為字符串類型。對象的鍵在前面的章節有詳細的介紹,這裡不再重復。
創建Symbol鍵
將Symbol
作為鍵值有兩種方法:
例1:
let id = Symbol('id'); let user = {}; user[id] = 'id value';//添加Symbol鍵 console.log(user[id]);//id value
例2:
let id = Symbol('id'); let user = { [id]:'id value',//註意這裡的方括號 }; console.log(user[id]);
以上兩個案例展示瞭在對象中插入Symbol
類型作為鍵的用法,需要註意的是,在訪問屬性時需要使用obj[id]
而不是obj.id
,因為obj.id
代表的是obj[‘id’]
。
如果我們使用Symbol
作為對象的鍵會有什麼效果呢?
for…in中被跳過
Symbol
非常明顯的一個特征是,如果對象中使用Symbol
作為鍵,那麼使用for…in
語句是訪問不到Symbol
類型的屬性的。
舉個例子:
let id = Symbol('id'); let user = { name : 'xiaoming', [id] : 'id', }; for (let key in user) console.log(user[key]);
執行以上代碼,得到以下結果:
> xiaoming
可以發現,[id]
對象的值沒有被打印出來,說明在對象屬性列表中,使用for … in
會自動忽略Symbol
類型的鍵。
同樣的,Object.keys(user)
也會忽略所有的Symbol
類型的鍵。
這樣的特性能帶來非常有用的效果,例如我們可以創建隻能自己能用的屬性。
雖然我們沒有辦法直接獲取到Symbol
鍵,但是Object.assign
方法能夠復制所有的屬性:
let id = Symbol(); let obj = { [id] : '123' } let obj2 = Object.assign({},obj); console.log(obj2[id]);
這並不影響Symbol
的隱藏屬性,因為復制後的對象仍然無法獲取Symbol
鍵。
隱藏自定義屬性
由於Symbol
既不能直接轉為字符串,我們沒有辦法直觀的獲得它的值,又不能通過for … in
獲得對象的Symbol
屬性,也就是說,如果沒有Symbol
變量本身,我們就沒有辦法獲得對象內部的對應屬性。
因此,通過Symbol
類型的鍵值,我們可以隱藏屬性,這些屬性隻能我們自己訪問,其他人都看不到我們的屬性。
舉個例子:
我們在開發的過程中,需要和同事“張三”合作,而這個張三創建瞭一個非常好用的工具Tool
,Tool
是一個對象類型,我們想白嫖張三的Tool
,並在此基礎上添加一些自己的屬性。
我們就可以通過添加Symbol
類型的鍵:
let tool = {//張三寫好瞭的Tool usage : "Can do anything", } let name = Symbol("My tool obj"); tool[name] = "This is my tool"; console.log(tool[name]);
以上示例展示瞭如何在別人寫好的對象上添加自己的屬性,那麼為什麼要使用Symbol
類型而不是常規的字符串呢?
原因如下:
- 對象
tool
是別人寫好的代碼,原則上我們不應該去修改別人的代碼,這樣會造成風險; - 避免命名沖突,我們直接使用字符串很有可能會和別人原有的屬性鍵沖突,造成嚴重的後果;使用
Symbol
永遠不會發生命名沖突,因為Symbol
都是不同的; - 別人無法訪問
Symbol
類型的鍵,相當於不會和別人的代碼沖突;
錯誤示范:
如果我們不使用Symbol
類型,很可能出現以下情況:
let tool = {//張三寫好瞭的Tool usage : "Can do anything", } tool.usage = "Boom Boom"; console.log(tool.usage);
以上代碼由於重復使用”usage”,從而重寫瞭原屬性,會造成對象原功能異常。
Symbol全局註冊表
所有的Symbol
變量都是不同的,即使他們有用相同的標簽(描述)。
有些時候,我們希望通過一個字符串名稱(標簽),訪問同一個Symbol
對象,例如我們在代碼的不同地方訪問相同的Symbol
。
JavaScript
會維護一個全局的Symbol
註冊表,我們可以通過向註冊表中插入Symbol
對象,並為對象起一個字符串名稱訪問該對象。
向註冊表插入或者讀取Symbol
對象需要使用Symbol.for(key)
方法,如果註冊表中有名為key
的對象,就返回該對象,否則就插入新對象再返回。
舉個例子:
let id1 = Symbol.for('id');//註冊表內沒有名為id的Symbol,創建並返回 let id2 = Symbol.for('id');//註冊表內已有名為id的Symbol,直接返回 console.log(id1===id2);//true
我們通過Symbol.for(key)
就能以全局變量的方式使用Symbol
對象,並使用一個字符串標記對象的名字。
相反的,我們還可以使用Symbol.keyFor(Symbol)
反向的從對象獲取名稱。
舉個例子:
let id = Symbol.for('id');//註冊表內沒有名為id的Symbol,創建並返回 let name = Symbol.keyFor(id); console.log(name);//id
Symbol.keyFor()
函數隻能用在全局Symbol
對象上(使用Symbol.for
插入的對象),如果用在非全局對象上,就會返回undefined
。
舉個例子:
let id = Symbol('id');//局部Symbol let name = Symbol.keyFor(id); console.log(name);//undefined
系統Symbol
JavaScript
有許多系統Symbol
,例如:
Symbol.hasInstance
Symbol.iterator
Symbol.toPrimitive
它們各有用途,我們在後面的會逐步介紹道這些獨特的變量。
總結
Symbol
對象的值是唯一的;Symbol
可以添加一個標簽,並通過標簽在全局註冊表中查詢對象的實體;Symbol
作為對象的鍵無法被for … in
探測到;- 我們可以通過
Symbol
到全局註冊表訪問全局的Symbol
對象;
但是,Symbol
並不是完全隱藏的,我們可以通過Object.getOwnPropertySymbols(obj)
獲取對象所有的Symbol
,或者通過Reflect.ownKeys(obj)
獲取對象所有的鍵。
到此這篇關於JavaScript的Symbol類型、隱藏屬性、全局註冊表的文章就介紹到這瞭,更多相關js Symbol類型、隱藏屬性、全局註冊表內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- JavaScript原始值與包裝對象的詳細介紹
- JavaScript的八種數據類型
- 分享JavaScript 類型判斷的幾種方法
- JavaScript中檢測數據類型的四種方法
- javascript遍歷對象的五種方式實例代碼