JavaScript的Symbol型別、隱藏屬性及全域性登入檔詳解

2022-06-02 14:00:42
本篇文章給大家帶來了關於的相關知識,其中主要介紹了關於Symbol型別、隱藏屬性及全域性登入檔的相關問題,包括了Symbol型別的描述、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的規範,只有兩種型別的值可以作為物件的屬性鍵:

  1. 字串
  2. 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型別的鍵值,我們可以隱藏屬性,這些屬性只能我們自己存取,其他人都看不到我們的屬性。

舉個例子:

我們在開發的過程中,需要和同事「張三」合作,而這個張三建立了一個非常好用的工具ToolTool是一個物件型別,我們想白嫖張三的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型別而不是常規的字串呢?

原因如下:

  1. 物件tool是別人寫好的程式碼,原則上我們不應該去修改別人的程式碼,這樣會造成風險;
  2. 避免命名衝突,我們直接使用字串很有可能會和別人原有的屬性鍵衝突,造成嚴重的後果;
  3. 使用Symbol永遠不會發生命名衝突,因為Symbol都是不同的;
  4. 別人無法存取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

它們各有用途,我們在後面的會逐步介紹道這些獨特的變數。

總結

  1. Symbol物件的值是唯一的;
  2. Symbol可以新增一個標籤,並通過標籤在全域性登入檔中查詢物件的實體;
  3. Symbol作為物件的鍵無法被for … in探測到;
  4. 我們可以通過Symbol到全域性登入檔存取全域性的Symbol物件;

但是,Symbol並不是完全隱藏的,我們可以通過Object.getOwnPropertySymbols(obj)獲取物件所有的Symbol,或者通過Reflect.ownKeys(obj)獲取物件所有的鍵。

【相關推薦:、】

以上就是JavaScript的Symbol型別、隱藏屬性及全域性登入檔詳解的詳細內容,更多請關注TW511.COM其它相關文章!