本節將會重點分析ES6引入的第6種基本類型:Symbol(符號)。符號可以像字元串那樣作為對象的屬性名,只是它有唯一性的特點,可以避免屬性名之間的衝突。

一、創建

  符號沒有字面量形式,只能通過Symbol()函數創建。該函數有一個可選的參數,只是用來描述當前符號,除了便於閱讀之外,沒有其他用途。由此可知,即使兩個符號的描述相同,它們還是不能畫等號。注意,Symbol()不是構造函數,因此不能和new運算符組合使用,否則會拋出類型錯誤。下面用一個例子展示符號的創建。

var sym1 = Symbol(),
sym2 = Symbol("name"),
sym3 = Symbol("name"),
sym4 = new Symbol(); //拋出類型錯誤
console.log(sym2 === sym3); //false

  如果要識別一個變數是否為符號,可以用typeof運算符。ES6擴展了它,當檢測到符號時,能返回一個新的類型字元串「symbol」,具體如下所示。

typeof sym1; //"symbol"
typeof sym2; //"symbol"

二、類型轉換

  符號在類型轉換時表現得並不靈活,它無法與數字或字元串進行運算,也無法顯式的轉換成數字。如下所示,後面四條語句在執行時都會報錯。

var sym = Symbol("age");
Number(sym);
parseInt(sym);
1 + sym;
"" + sym;

  不過,符號可以顯式的轉換成字元串或布爾值,具體如下所示。

Boolean(sym);   //true
!sym;   //false
sym.toString(); //"Symbol(age)"
String(sym); //"Symbol(age)"

三、全局共享

  ES6會在內部維護一張全局符號註冊表,通過Symbol.for()方法,可以登記指定符號,使其變成一個全局有效地符號,從而達到全局共享。該方法只接收一個參數,這個參數既是註冊表中的鍵值,同時也是此符號的描述。下面的代碼調用了兩次Symbol.for()方法,傳遞了相同的參數,返回的卻是同一個全局符號。

var sym1 = Symbol.for("name"),
sym2 = Symbol.for("name");
console.log(sym1 === sym2); //true

  在上面的代碼中,第一次調用Symbol.for()方法時,會在註冊表中搜索鍵值為「name」的符號,由於沒有找到,所以就會創建一個新的符號。而在第二次調用Symbol.for()方法時,由於傳遞的鍵值與前一次相同,因此會返回剛剛的那個符號。從而可知,對變數sym1和sym2進行全等比較,返回的結果將是true。

  如果要獲取某個全局符號所對應的鍵值(即它的描述),那麼可以通過Symbol.keyFor()實現,具體操作如下所示。

Symbol.keyFor(sym1); //"name"
Symbol.keyFor(sym2); //"name"

四、屬性名

  本節開頭曾提到過對象的屬性名可以用符號表示,而這類屬性可以有三種賦值方式。第一種是用方括弧,注意,不能用點號,因為點號後面的標識符會被識別成字元串而不是符號。下面代碼分別用方括弧和點號為obj對象的sym屬性賦值,前者被識別成了符號屬性,而後者卻被識別成了字元串屬性。

var sym = Symbol("name"),
obj = {};
obj[sym] = "strick";
obj.sym = "strick";
console.log(obj); //{Symbol(name): "strick", sym: "strick"}

  第二種是在創建對象字面量時,用計算屬性名的方式(即屬性名被方括弧包裹)為其賦值,如下所示。

obj = {
[sym]: "freedom"
};

  第三種是調用Object.defineProperty()或Object.defineProperties()方法來為符號屬性賦值,如下所示。

Object.defineProperty(obj, sym, { value: "justice" });

  注意,符號屬性是不可枚舉的,既不能被for-in等循環遍歷到,也不能被Object.keys()、Object.getOwnPropertyNames()等方法讀取到。但可以通過Object.getOwnPropertySymbols()方法獲得對象的符號屬性,具體如下所示。

obj = {
[sym]: "freedom",
age: 28
};
Object.keys(obj);    //["age"]
Object.getOwnPropertyNames(obj); //["age"]
Object.getOwnPropertySymbols(obj); //[Symbol(name)]

五、內置符號

  ES6提供了一些內置符號,也叫做知名符號(Well-Known Symbol)。它們暴露了語言的內部邏輯,允許開發人員修改或拓展規範所描述的對象特徵或行為。每一個內置符號對應Symbol對象的一個屬性,例如Symbol.hasInstance、Symbol.iterator等,表1列出了11個內置符號。

表1 內置符號

  下面會給出4個內置符號的示例,分別是hasInstance、isConcatSpreadable、match和toStringTag。

let digit = {
[Symbol.hasInstance](number) {
return !(number % 2); //判斷數字是否為偶數
}
};
1 instanceof digit; //false
2 instanceof digit; //true

let arr1 = [3, 4];
[1, 2].concat(arr1); //[1, 2, 3, 4]
let arr2 = [3, 4];
arr2[Symbol.isConcatSpreadable] = false; //禁止展開
[1, 2].concat(arr2); //[1, 2, [3, 4]]

let regex = {
[Symbol.match](str) {
return str.substr(0, 10);
}
},
message = "My name is strick";
message.match(regex);  //"My name is"

let people = {
[Symbol.toStringTag]: "People"
};
people.toString(); //"[object People]"

推薦閱讀:

相关文章