javascript
第十一节:JavaScript有了一种全新的数据类型:Symbol
????????連續連載了幾篇《ES6對xxx的擴展》,本節咱們換換口味,介紹一種全新的數據類型:Symbol,中文意思為:標志,記號。音標:[?s?mb?l]。
數據類型
????????在介紹Symbol之前,我們簡單介紹一下JavaScript的數據類型:
????
????????JavaScript有6中數據類型,分別是:
String?字符串類型
Number?數字類型
Object?對象類型
Boolean?布爾值類型
Null??空值
Undefined?未定義
????????這6種類型寫過代碼的同學都不會陌生,它們都有各自的用途。而ES6給我們帶來一種全新的數據類型:Symbol。
????????每一種全新的事物的誕生都是為了解決某種問題。
設計初衷
????????為了探索它的設計初衷,我們聊聊一個實際的開發場景:
????????在一個多人合作的團隊中,程序員A寫了一個對象Object供其他人使用,有一天程序員B使用了這個對象Object,還為它添加了幾個新的屬性和方法,一切都那么順利地完成了。
????????次日,測試告訴A產品有bug,A:“怎么可能,昨天還好好的,我都沒改過任何東西啊~~~”。
?
????????無可奈何的A只要慢慢排查,最后發現,是B給對象Object添加方法的時候,其中一個方法名和A寫的一個方法名相同,被覆蓋了。
????????對象的屬性被覆蓋,這在日常開發中,也時常會出現,為了根本上解決命名問題,我們需要給屬性或者方法起一個獨一無二的名稱,這樣,才能從根本上防止屬性名沖突的問題。
????????這就是ES6設計出一個Symbol的初衷:解決對象的屬性名沖突。既然我們知道了Symbol的設計初衷,也就是知道了它的作用。接下來,我們來看看它是什么使用的:
??//定義一個symbol類型的變量
????let?sm?=?Symbol();
????console.log(sm);
????//打印結果:Symbol()
????console.log(typeof?sm);
????//打印結果:symbol
????????從上面代碼案例看到,我們用一個Symbol(?)函數來創建一個symbol類型的變量,我們打印了一下變量sm,得到的結果是控制臺輸出:Symbol(?),它代表著一個獨一無二的值,雖然我們看不到它長什么樣子,但基本上,它有點類似字符串。
????????接著,我們用typeof來檢測一下變量sm的類型,得到的結果是:symbol。
?
????????怎樣判斷是它是獨一無二的值呢?我們來看看:
????let sm1?=?Symbol();
????let?sm2?=?Symbol();
????sm1?===?sm2?//結果:false
????console.log(sm1);//結果:Symbol()
????console.log(sm2);//結果:Symbol()
????????我們定義兩個symbol類型的變量sm1,sm2,然后用全等符號===(也稱嚴格相等,第九節講解過)進行比較,得到的是false。也就是他們都是獨一無二的值,并不相等。
????????接著,我們分別打印兩個變量,控制臺輸出的都是Symbol(?),看起來長得一模一樣,實際是不相等的。
????????兩個不一樣的值,控制臺輸出的一樣,這樣無疑給我們開發調試帶來一定的不便,有沒有辦法讓他們看起來不一樣呢?
????????有的,Symbo(?)函數接受參數,用于對實例值的描述。我們試試看:
????let?sm1?=?Symbol('sm1');
????let?sm2?=?Symbol('sm2');
????console.log(sm1);
????//結果:Symbol(sm1)
????console.log(sm2);
????//結果:Symbol(sm2)
????????用字符串sm1和sm2作為參數,結果打印出來的變量sm1和sm2就是Symbol(sm1)和Symbol(sm2),等于加上了描述,很容易區分出來。
????????需要注意的是,即使參數一樣,描述一樣,得到的兩個值也是不相等的,不信我們來看看:
????let sm1?=?Symbol('sm');
????let?sm2?=?Symbol('sm');
????sm1?===?sm2?//結果:false
????????即使兩個變量的描述都是“sm”,但是終究對應的值還是不一樣的,symbol永遠都是獨一無二的值,謹記。
?
????????了解了這幾個symbol類型值的特點后,前面說到,Symbol是為了解決對象屬性名沖突的問題,那么我們就結合對象,來學習:
????let?name?=?Symbol();
????let?person?=?{
????????[name]:"張三"
????};
????console.log(person[name]);
????//結果:張三
????console.log(person.name);
????//結果:undefined
????????看代碼,從上往下擼,首先,我們定義一個symbol類型的變量name,它作為一個對象person的屬性,對應的值是“張三”;
????????
????????接著,我們用兩種方式獲取name的值,第一種用中括號的形式[?name?]能正確獲取到,第二種用點運算符的形式,獲取失敗。原因是:當symbol值作為對象的屬性名的時候,不能用點運算符獲取對應的值。
????????此外還有一點要注意,把一個symbol類型的值作為對象的屬性名的時候,一定要用中括號[?],不能用點運算符,因為用點運算符的話,會導致javascript把后面的屬性名為理解為一個字符串類型,而不是symbol類型。具體看代碼:
????let?name?=?Symbol();
????let?person?=?{};
????person.name?=?"張三";
????person[name];???//結果:undefined
????person['name'];?//結果:張三
????person.name;????//結果:張三
????????其中變量name是symbol,但是給person對象設置屬性的時候,用的是點運算符person.name,而不是中括號person[?name?],這會有什么后果呢?這就會導致person對象中的屬性name實際上是字符串類型的,這也就解釋了最后三行代碼的打印結果了。
????????person[?name?]這句代碼相當于要求javascript去person對象內找一個symbol類型的屬性name,不好意思,沒有,找不到。person對象只有一個字符串類型的屬性name;所以,如果用person[‘name’]或者peroson.name獲取的話,就能找到對應的屬性name了。
????????原來用symbol類型的值作為對象的屬性也有這么多講究,好吧,我認了!誰叫你是ECMAScript呢,你說了算!
????????用symbol類型的屬性除了能保證是獨一無二的值,還有什么其他的特點嗎?
屬性名的遍歷
????????當symbol類型的值作為屬性名的時候,該屬性是不會出現在for...in和for...of中的,也不會被Object.keys(?)獲取到。我們來看案例:
??//定義一個symbol類型的變量name
????let?name?=?Symbol();
????//定義一個含有兩種類型屬性的對象
????let?person?=?{
????????[name]:"張三",??//symbol類型
????????"age":12????????//string類型
????};
????Object.keys(person);//結果:["age"]
????for(let?key?in?person){
????????console.log(key);
????}
????//打印結果:age
????????person對象有兩個屬性,屬性名有兩種類型:symbol類型和string字符串類型,我們通過keys(?)函數獲取到的屬性,只有屬性age,我們通過for...in循環打印出來,也只打印出了屬性age。(for...of也屬于ES6的新增知識,后面會專門有一節介紹),以上幾種方法都無法獲取到symbol類型的屬性。
getOwnPropertySymbols(?)函數
????????如果我們硬是想要獲取symbol類型的屬性怎么辦?我們可以用一個方法:Object.getOwnPropertySymbols(?),它會找到symbol類型的屬性并且返回一個數組,數組的成員就是symbol類型的屬性值,看代碼:
??//定義兩個symbol類型的變量name,age
????let?name?=?Symbol("name");
????let?age?=?Symbol("age");
????let?person?=?{
????????[name]:"張三",?//symbol類型
????????[age]:12???????//symbol類型
????};
????Object.getOwnPropertySymbols(person);
????//結果:[Symbol(name),?Symbol(age)]
????????person對象的兩個屬性都是symbol類型的,我們也知道用Object.keys(?)和for...in都無法獲取到它,我們就用getOwnPropertySymbols(?)方法來,結果成功了,得到一個數組,數組的內容就是兩個symbol類型變量對應的值Symbol(name)和?Symbol(age)。
?
Reflect.ownKeys(?)函數
????????這樣的話,獲取字符串類型的屬性和獲取symbol類型的屬性要分開兩種不同的方式來獲取,難免有有時候會很不方便,有木有什么辦法讓我們一次性獲取所有類型的屬性,不管它是字符串類型還是symbol類型呢?
????????有的,我們可以用Reflect.ownKeys(?)方法實現:
??//定義一個對象,含有兩種類型的屬性
????let?person?=?{
????????[Symbol('name')]:"張三",
????????"age":?21
????};
????Reflect.ownKeys(person);
????//結果:["age",Symbol(name)]
????????上面的代碼中,我們先定義一個對象person,它含有兩個屬性,一個是symbol類型的,一個是字符串類型的。
????????接著,我們將對象person傳入Reflect.ownKeys(?)函數中,函數就會給我們返回一個數組,數組的內容便是對象的屬性,包括symbol類型和字符串類型。
?
????????此外,Symbol還提供了兩個很實用的函數,我們來學習一下。
Symbol.for(?)函數
????????函數作用:根據參數名,去全局環境中搜索是否有以該參數為名的symbol值,有就返回它,沒有就以該參數名來創建一個新的symbol值。
????????文字描述總是那么乏力,所以要加上案例:
????let?n1?=?Symbol.for('name');
????let?n2?=?Symbol.for('name');
????console.log(n1?===?n2);
????//結果:true
????????上面最后一句代碼,我們用全相等來對兩個變量進行對比,得到:true;說明n2就是n1,兩者相等。
????????但是細心地同學會注意到,上面的代碼中,定義兩個symbol值得時候用的都是Symbol.for(?),而不是用Symbol(?)。
????????兩者在創建symbol值的時候有什么不同嗎?它們的區別是:Symbol.for(?)創建的symbol值會被登記在全局環境中,供以后用Symbol.for(?)來搜索,而Symbol(?)創建的變量就沒有這樣的效果了。
????????也就是說,用Symbol(?)創建的symbol值,以后用Symbol.for(?)去搜索,是找不到的。不信,我們來演示一下,還是用上面的代碼,我們稍微改一下第一行:
????let?n1?=?Symbol('name');
????let?n2?=?Symbol.for('name');
????console.log(n1?===?n2);
????//結果:false
????????第一行我們用Symbol(?)來創建的一個symbol值,按照上述的所說的,它不會被登記在全局環境中;所以,第二行我們用Symbol.for(?)去找的時候,是找不到的,找不到怎么辦?此時Symbol.for(?)會自動創建一個新的symbol值,也就是說n1,n2是不同的兩個symbol值了,所以進行全相等比較的時候,會返回:false。
?
Symbol.keyFor(?)函數
????????函數作用:返回一個以被登記在全局環境中的symbol值的key,沒有就返回undefined。注意這句話的一個關鍵詞:“被登記在全局環境中”,也就是說這個symbol值是被Symbol.for(?)創建的,不是被Symbol(?)創建的。
????let?n1?=?Symbol.for('name');
????Symbol.KeyFor(n1);
????//結果:name
????????上面的變量n1是被Symbol.for(?)創建,不是被Symbol(?)創建的,所以用Symbol.keyFor(?)去找,是能找到的,會返回這個symbol值的key,也就是它的描述:name。
????????我們再對上面的案例稍做修改:
????
????let?n1?=?Symbol('name');
????Symbol.KeyFor(n1);
????//結果:undefined
????????這段代碼的變量n1是用Symbol(?)創建的,最后的結果是undefined;這就證明了兩個知識點:1、Symbol(?)創建symbol值不會被登記在全局環境中供Symbol.for(?)和Symbol.keyFor(?)搜索;2、Symbol.keyFor(?)函數在全局環境中找不到對應的symbol,就回返回undefined。
?
????????以上就是ES6給我們帶來的第七種數據類型:Symbol;Symbol還有更多的小知識,初學者或者新手只要掌握理解上面的幾點就足夠日常使用了,只要進了門,往后的深入都是自然而然的,任何知識的學習都一樣。
本節小結
總結:JavaScript有了第七種數據類型:Symbol,創建一個獨一無二的值;它用于對象的屬性,設計初衷是為了避免對象屬性沖突的問題。要獲取對象symbol類型的屬性,要用Object.getOwnPropertySymbols(?);還提供了Symbol.for(?)和Symbol.keyFor(?)方法用于搜索對應的symbol值。
?
總結
以上是生活随笔為你收集整理的第十一节:JavaScript有了一种全新的数据类型:Symbol的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 国学早教视频 16G
- 下一篇: 1000多首无损歌曲合集