本節(jié)將會重點分析ES6引入的第6種基本類型:Symbol(符號)。符號可以像字符串那樣作為對象的屬性名,只是它有唯一性的特點,可以避免屬性名之間的沖突。
一、創(chuàng)建
符號沒有字面量形式,只能通過Symbol()函數(shù)創(chuàng)建。該函數(shù)有一個可選的參數(shù),只是用來描述當(dāng)前符號,除了便于閱讀之外,沒有其他用途。由此可知,即使兩個符號的描述相同,它們還是不能畫等號。注意,Symbol()不是構(gòu)造函數(shù),因此不能和new運算符組合使用,否則會拋出類型錯誤。下面用一個例子展示符號的創(chuàng)建。
var sym1 = Symbol(), sym2 = Symbol("name"), sym3 = Symbol("name"), sym4 = new
Symbol();//拋出類型錯誤 console.log(sym2 === sym3); //false
如果要識別一個變量是否為符號,可以用typeof運算符。ES6擴(kuò)展了它,當(dāng)檢測到符號時,能返回一個新的類型字符串“symbol”,具體如下所示。
typeof sym1; //"symbol" typeof sym2; //"symbol"
二、類型轉(zhuǎn)換
符號在類型轉(zhuǎn)換時表現(xiàn)得并不靈活,它無法與數(shù)字或字符串進(jìn)行運算,也無法顯式的轉(zhuǎn)換成數(shù)字。如下所示,后面四條語句在執(zhí)行時都會報錯。
var sym = Symbol("age"); Number(sym); parseInt(sym); 1 + sym; "" + sym;
不過,符號可以顯式的轉(zhuǎn)換成字符串或布爾值,具體如下所示。
Boolean(sym); //true !sym; //false sym.toString(); //"Symbol(age)"
String(sym);//"Symbol(age)"
三、全局共享
ES6會在內(nèi)部維護(hù)一張全局符號注冊表,通過Symbol.for()方法,可以登記指定符號,使其變成一個全局有效地符號,從而達(dá)到全局共享。該方法只接收一個參數(shù),這個參數(shù)既是注冊表中的鍵值,同時也是此符號的描述。下面的代碼調(diào)用了兩次Symbol.for()方法,傳遞了相同的參數(shù),返回的卻是同一個全局符號。
var sym1 = Symbol.for("name"), sym2 = Symbol.for("name"); console.log(sym1 ===
sym2);//true
在上面的代碼中,第一次調(diào)用Symbol.for()方法時,會在注冊表中搜索鍵值為“name”的符號,由于沒有找到,所以就會創(chuàng)建一個新的符號。而在第二次調(diào)用Symbol.for()方法時,由于傳遞的鍵值與前一次相同,因此會返回剛剛的那個符號。從而可知,對變量sym1和sym2進(jìn)行全等比較,返回的結(jié)果將是true。
如果要獲取某個全局符號所對應(yīng)的鍵值(即它的描述),那么可以通過Symbol.keyFor()實現(xiàn),具體操作如下所示。
Symbol.keyFor(sym1); //"name" Symbol.keyFor(sym2); //"name"
四、屬性名
本節(jié)開頭曾提到過對象的屬性名可以用符號表示,而這類屬性可以有三種賦值方式。第一種是用方括號,注意,不能用點號,因為點號后面的標(biāo)識符會被識別成字符串而不是符號。下面代碼分別用方括號和點號為obj對象的sym屬性賦值,前者被識別成了符號屬性,而后者卻被識別成了字符串屬性。
var sym = Symbol("name"), obj = {}; obj[sym] = "strick"; obj.sym = "strick";
console.log(obj);//{Symbol(name): "strick", sym: "strick"}
第二種是在創(chuàng)建對象字面量時,用計算屬性名的方式(即屬性名被方括號包裹)為其賦值,如下所示。
obj = { [sym]: "freedom" };
第三種是調(diào)用Object.defineProperty()或Object.defineProperties()方法來為符號屬性賦值,如下所示。
Object.defineProperty(obj, sym, { value: "justice" });
注意,符號屬性是不可枚舉的,既不能被for-in等循環(huán)遍歷到,也不能被Object.keys()、Object.getOwnPropertyNames()等方法讀取到。但可以通過Object.getOwnPropertySymbols()方法獲得對象的符號屬性,具體如下所示。
obj = { [sym]: "freedom", age: 28 }; Object.keys(obj); //["age"]
Object.getOwnPropertyNames(obj);//["age"] Object.getOwnPropertySymbols(obj); //
[Symbol(name)]
五、內(nèi)置符號
ES6提供了一些內(nèi)置符號,也叫做知名符號(Well-Known
Symbol)。它們暴露了語言的內(nèi)部邏輯,允許開發(fā)人員修改或拓展規(guī)范所描述的對象特征或行為。每一個內(nèi)置符號對應(yīng)Symbol對象的一個屬性,例如Symbol.hasInstance、Symbol.iterator等,表1列出了11個內(nèi)置符號。
表1? 內(nèi)置符號
屬性名稱 值類型 描述
hasInstance 方法 當(dāng)使用instanceof運算符時會調(diào)用該方法
isConcatSpreadable 布爾值 當(dāng)對象作為Array.prototype.concat()方法的參數(shù)時,控制該對象是否被展開
iterator 方法 返回一個迭代器,用于定義一個可迭代的對象
match 方法 當(dāng)對象作為String.prototype.match()方法的參數(shù)時,該方法會被調(diào)用
replace 方法 當(dāng)對象作為String.prototype.replace()方法的參數(shù)時,該方法會被調(diào)用
search 方法 當(dāng)對象作為String.prototype.search()方法的參數(shù)時,該方法會被調(diào)用
split 方法 當(dāng)對象作為String.prototype.split()方法的參數(shù)時,該方法會被調(diào)用
species 方法 創(chuàng)建派生類的構(gòu)造函數(shù)
toPrimitive 方法 當(dāng)對象需要轉(zhuǎn)換成原始值(即執(zhí)行ToPrimitive抽象操作)時,該方法會被調(diào)用
toStringTag 字符串 指定對象的類型,可在調(diào)用Object.prototype.toString()方法的時候返回
unscopables 對象 保存在這個對象中的屬性將不能被with語句所引用
下面會給出4個內(nèi)置符號的示例,分別是hasInstance、isConcatSpreadable、match和toStringTag。
let digit = { [Symbol.hasInstance](number) { return !(number % 2); //判斷數(shù)字是否為偶數(shù)
} };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]"
?
熱門工具 換一換