天天看點

JS Symbol類型大揭秘,為什麼面試都喜歡問?

在JS中,有一個資料類型大家可能用的不太多,但是面試出現的頻率很高,那就是ES6引入的Symbol類型。

一、Symbol概述

ES5的對象屬性名都是字元串,這很容易造成屬性名的沖突。 比如,你使用了一個他人提供的對象,但又想為這個對象添加新的方法,新方法的名字就有可能與現有方法産生沖突。如果有一種機制,

保證每個屬性的名字都是獨一無二的

就好了,這樣就從根本上防止屬性名的沖突。這就是ES6引入的Symbol的原因。

ES6引入了一種新的

原始資料類型Symbol

,表示獨一無二的值。它是JavaScript語言的第七種資料類型,前六種是:undefined、null、布爾值(Boolean)、字元串(String)、數值(Number)、對象(Object)。

Symbol值通過

Symbol函數

生成。這就是說,對象的屬性名現在可以有兩種類型,一種是原來就有的字元串,另一種就是新增的Symbol類型。凡是屬性名屬于Symbol類型,就

都是獨一無二的,可以保證不會與其他屬性名産生沖突。

let Sym = Symbol();

console.log(typeof Sym)
// symbol
}
           

上面代碼中,

變量s

就是獨一無二的值。typeof運算符的結果,

表明變量s是Symbol資料類型

,而不是字元串之類的其它類型。

注意,Symbol函數前不能使用new指令,否則會報錯。這是因為生成的Symbol是一個原始類型的值,不是對象。也就是說,由于Symbol值不是對象,是以不能添加屬性。基本上,它是一種類似于字元串的資料類型。
let Sym = new Symbol();

console.log(Sym)
//Uncaught TypeError: Symbol is not a constructor
//at new Symbol (<anonymous>)
           

Symbol函數可以接受一個字元串作為參數,表示對Symbol執行個體的描述,注意這個字元串僅僅表示描述而已,有了描述,輸出的時候就能夠厘清,到底是哪一個值。

let s1 = Symbol('foo');
let s2 = Symbol('bar');

s1 // Symbol(foo)
s2 // Symbol(bar)

s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"
           
是以相同參數的Symbol函數的傳回值是不相等的。
let s1 = Symbol('foo');
let s2 = Symbol('foo');

console.log(s1 === s2)
//false
           

Symbol值不能與其它類型的值進行運算,會報錯。

let sym = Symbol('My symbol');

"your symbol is " + sym
// TypeError: can't convert symbol to string
`your symbol is ${sym}`
// TypeError: can't convert symbol to string
           

但是,Symbol值可以顯式轉為字元串。

let sym = Symbol('My symbol');

String(sym) // 'Symbol(My symbol)'
sym.toString() // 'Symbol(My symbol)'
           

另外,Symbol值也可以轉為布爾值,但是不能轉為數值。

let sym = Symbol();
Boolean(sym) // true
!sym  // false

if (sym) {
  // ...
}
Number(sym) // TypeError
sym + 2 // TypeError
           

二、Symbol作為屬性名

由于每一個Symbol值都是不相等的,這意味着

Symbol值可以作為辨別符,用于對象的屬性名

,就能保證不會出現同名的屬性。這對于一個對象由多個子產品構成的情況非常有用,能防止某一個鍵被不小心改寫或覆寫。

let mySymbol = Symbol("mySymbol");
let a = {
	mySymbol:"name"
};
a[mySymbol] = 'Hello!';

console.log(a)
           
JS Symbol類型大揭秘,為什麼面試都喜歡問?
注意,Symbol值作為對象屬性名時,不能用點運算符。
const mySymbol = Symbol();
const a = {};
a.mySymbol = 'Hello!';
a[mySymbol] // undefined
a['mySymbol'] // "Hello!"
           

上面代碼中,因為點運算符後面總是字元串,是以不會讀取mySymbol作為辨別名所指代的那個值,導緻a的屬性名實際上是一個字元串,而不是一個Symbol值。

同理,在對象的内部,使用Symbol值定義屬性時,

Symbol值必須放在方括号之中。

let s = Symbol();
let obj = {
  [s]: function (arg) { ... }
};

obj[s](123);