
Symbol類型是JavaScript中的一種特殊的類型,特殊在所有的Symbol類型值都互不相同。我們可以使用「Symbol」來表示唯一的值,以下是創建Symbol物件的案例:
let id = Symbol();
這樣我們就創建了一個Symbol類型的值,並且把這個值儲存在了變數id中。
我們在創建一個Symbol類型變數的時候,可以在參數中傳入一些秒屬性的字串,用於描述這個變數的用途資訊。
例如:
let id1 = Symbol('狂拽酷炫吊炸天的小明的id');
let id2 = Symbol('低調奢華有內涵的婷婷的id'); Symbol類型在任何時候都是不同的,即使他們擁有相同的描述訊息,描述只是一個標籤,除此之外就沒有別的用途了,例如:
let id1 = Symbol('id');
let id2 = Symbol('id');
console.log(id1==id2);//false這個標籤存在的意義,個人認為和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);//加油,奧利給” 根據JavaScript的規範,只有兩種類型的值可以作為對象的屬性鍵:
如果使用其他類型,則會隱式的轉為字串
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作為物件的鍵會有什麼效果呢?
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 。
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);//undefinedJavaScript有許多系統Symbol ,例如:
Symbol.hasInstanceSymbol.iteratorSymbol.toPrimitive它們各有用途,我們在後面的會逐步介紹道這些獨特的變數。
Symbol物件的值是唯一的;Symbol可以添加一個標籤,並透過標籤在全域註冊表中查詢物件的實體;Symbol作為物件的按鍵無法被for … in偵測到;Symbol到全域註冊表訪問全域的Symbol物件;但是, Symbol並不是完全隱藏的,我們可以透過Object.getOwnPropertySymbols(obj)取得物件所有的Symbol ,或透過Reflect.ownKeys(obj)取得物件所有的鍵。