JavaScript有了一種全新的資料類型:Symbol

來源:互聯網
上載者:User

標籤:擷取對象   bsp   console   字串類型   blog   等於   eof   排查   參數   

資料類型

  在介紹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的設計初衷,也就是知道了它的作用。接下來,我們來看看它是什麼使用的:

 

1   //定義一個symbol類型的變數2     let sm = Symbol();3 4     console.log(sm);5     //列印結果:Symbol()6 7     console.log(typeof sm);8     //列印結果:symbol

從上面代碼案例看到,我們用一個Symbol( )函數來建立一個symbol類型的變數,我們列印了一下變數sm,得到的結果是控制台輸出:Symbol( ),它代表著一個獨一無二的值,雖然我們看不到它長什麼樣子,但基本上,它有點類似字串。

  接著,我們用typeof來檢測一下變數sm的類型,得到的結果是:symbol。

  怎樣判斷是它是獨一無二的值呢?我們來看看:

1 let sm1 = Symbol();2     let sm2 = Symbol();3 4     sm1 === sm2 //結果:false5 6     console.log(sm1);//結果:Symbol()7     console.log(sm2);//結果:Symbol()

我們定義兩個symbol類型的變數sm1,sm2,然後用全等符號===進行比較,得到的是false。也就是他們都是獨一無二的值,並不相等。

  接著,我們分別列印兩個變數,控制台輸出的都是Symbol( ),看起來長得一模一樣,實際是不相等的

   兩個不一樣的值,控制台輸出的一樣,這樣無疑給我們開發調試帶來一定的不便,有沒有辦法讓他們看起來不一樣呢?

   有的,Symbo( )函數接受參數,用於對執行個體值的描述。我們試試看:

1     let sm1 = Symbol(‘sm1‘);2     let sm2 = Symbol(‘sm2‘);3 4     console.log(sm1);5     //結果:Symbol(sm1)6 7     console.log(sm2);8     //結果:Symbol(sm2)

用字串sm1和sm2作為參數,結果列印出來的變數sm1和sm2就是Symbol(sm1)和Symbol(sm2),等於加上了描述,很容易區分出來。

  需要注意的是,即使參數一樣,描述一樣,得到的兩個值也是不相等的,不信我們來看看:

1     let sm1 = Symbol(‘sm‘);2     let sm2 = Symbol(‘sm‘);3 4     sm1 === sm2 //結果:false

 即使兩個變數的描述都是“sm”,但是終究對應的值還是不一樣的,symbol永遠都是獨一無二的值,謹記。

瞭解了這幾個symbol類型值的特點後,前面說到,Symbol是為瞭解決對象屬性名稱衝突的問題,那麼我們就結合對象,來學習:

 1     let name = Symbol(); 2     let person = { 3         [name]:"張三" 4     }; 5  6  7     console.log(person[name]); 8     //結果:張三 9 10     console.log(person.name);11     //結果:undefined

看代碼,從上往下擼,首先,我們定義一個symbol類型的變數name,它作為一個對象person的屬性,對應的值是“張三”;

 接著,我們用兩種方式擷取name的值,第一種用中括弧的形式[ name ]能正確擷取到,第二種用點運算子的形式,擷取失敗。原因是:當symbol值作為對象的屬性名稱的時候,不能用點運算子擷取對應的值

  此外還有一點要注意,把一個symbol類型的值作為對象的屬性名稱的時候,一定要用中括弧[ ],不能用點運算子,因為用點運算子的話,會導致javascript把後面的屬性名稱為理解為一個字串類型,而不是symbol類型。具體看代碼:

1     let name = Symbol();2     let person = {};3 4     person.name = "張三";5 6     person[name];   //結果:undefined7     person[‘name‘]; //結果:張三8     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( )擷取到。我們來看案例:

 1 //定義一個symbol類型的變數name 2     let name = Symbol(); 3  4     //定義一個含有兩種類型屬性的對象 5     let person = { 6         [name]:"張三",  //symbol類型 7         "age":12        //string類型 8     }; 9 10     Object.keys(person);//結果:["age"]11 12     for(let key in person){13         console.log(key);14     }15     //列印結果:age

  person對象有兩個屬性,屬性名稱有兩種類型:symbol類型和string字串類型,我們通過keys( )函數擷取到的屬性,只有屬性age,我們通過for...in迴圈列印出來,也只列印出了屬性age。(for...of也屬於ES6的新增知識,後面會專門有一節介紹),以上幾種方法都無法擷取到symbol類型的屬性。

getOwnPropertySymbols( )函數

  如果我們硬是想要擷取symbol類型的屬性怎麼辦?我們可以用一個方法:Object.getOwnPropertySymbols( ),它會找到symbol類型的屬性並且返回一個數組,數組的成員就是symbol類型的屬性值,看代碼:

 1  //定義兩個symbol類型的變數name,age 2     let name = Symbol("name"); 3     let age = Symbol("age"); 4  5  6     let person = { 7         [name]:"張三", //symbol類型 8         [age]:12       //symbol類型 9     };10 11     Object.getOwnPropertySymbols(person);12     //結果:[Symbol(name), Symbol(age)]

 person對象的兩個屬性都是symbol類型的,我們也知道用Object.keys( )和for...in都無法擷取到它,我們就用getOwnPropertySymbols( )方法來,結果成功了,得到一個數組,數組的內容就是兩個symbol類型變數對應的值Symbol(name)和 Symbol(age)。

 Reflect.ownKeys( )函數

 這樣的話,擷取字串類型的屬性和擷取symbol類型的屬性要分開兩種不同的方式來擷取,難免有有時候會很不方便,有木有什麼辦法讓我們一次性擷取所有類型的屬性,不管它是字串類型還是symbol類型呢?

    有的,我們可以用Reflect.ownKeys( )方法實現:

1   //定義一個對象,含有兩種類型的屬性2     let person = {3         [Symbol(‘name‘)]:"張三",4         "age": 215     };6 7     Reflect.ownKeys(person);8     //結果:["age",Symbol(name)]

上面的代碼中,我們先定義一個對象person,它含有兩個屬性,一個是symbol類型的,一個是字串類型的。

  接著,我們將對象person傳入Reflect.ownKeys( )函數中,函數就會給我們返回一個數組,數組的內容便是對象的屬性,包括symbol類型和字串類型。

此外,Symbol還提供了兩個很實用的函數,我們來學習一下。

 Symbol.for( )函數

 函數作用:根據參數名,去全域環境中搜尋是否有以該參數為名的symbol值,有就返回它,沒有就以該參數名來建立一個新的symbol值。

 文字描述總是那麼乏力,所以要加上案例:

1  let n1 = Symbol.for(‘name‘);2     let n2 = Symbol.for(‘name‘);3     console.log(n1 === n2);4     //結果:true

   上面最後一句代碼,我們用全相等來對兩個變數進行對比,得到:true;說明n2就是n1,兩者相等。

   但是細心地同學會注意到,上面的代碼中,定義兩個symbol值得時候用的都是Symbol.for( ),而不是用Symbol( )。

 兩者在建立symbol值的時候有什麼不同嗎?它們的區別是:Symbol.for( )建立的symbol值會被登記在全域環境中,供以後用Symbol.for( )來搜尋,而Symbol( )建立的變數就沒有這樣的效果了。

 也就是說,用Symbol( )建立的symbol值,以後用Symbol.for( )去搜尋,是找不到的。不信,我們來示範一下,還是用上面的代碼,我們稍微改一下第一行:

1     let n1 = Symbol(‘name‘);2     let n2 = Symbol.for(‘name‘);3     console.log(n1 === n2);4     //結果:false

    第一行我們用Symbol( )來建立的一個symbol值,按照上述的所說的,它不會被登記在全域環境中;所以,第二行我們用Symbol.for( )去找的時候,是找不到的,找不到怎麼辦?此時Symbol.for( )會自動建立一個新的symbol值,也就是說n1,n2是不同的兩個symbol值了,所以進行全相等比較的時候,會返回:false。

 Symbol.keyFor( )函數

        函數作用:返回一個以被登記在全域環境中的symbol值的key,沒有就返回undefined。注意這句話的一個關鍵詞:“被登記在全域環境中”,也就是說這個symbol值是被Symbol.for( )建立的,不是被Symbol( )建立的。

1     let n1 = Symbol.for(‘name‘);2     Symbol.KeyFor(n1);3     //結果:name

  上面的變數n1是被Symbol.for( )建立,不是被Symbol( )建立的,所以用Symbol.keyFor( )去找,是能找到的,會返回這個symbol值的key,也就是它的描述:name。

  我們再對上面的案例稍做修改:

1  2     let n1 = Symbol(‘name‘);3     Symbol.KeyFor(n1);4     //結果: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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.