前言
該部分為書籍 深入理解ES6 第六章(符號與符號屬性)筆記
創(chuàng)建符號值
- 符號沒有字面量形式, 這在
JS
的基本類型中是獨一無二的. 使用全局Symbol
函數(shù)來創(chuàng)建一個符號值 - 由于符號值是基本類型的值, 因此調(diào)用
new Symbol()
將會拋出錯誤. 你可以通過 new
Object(yourSymbol) 來創(chuàng)建一個符號實例,但尚不清楚這能有什么作用。 -
Symbol
函數(shù)還可以接受一個額外的參數(shù)用于描述符號值, 該描述不能用來訪問對應(yīng)屬性, 但它能用于調(diào)試. 符號的描述信息被存儲在內(nèi)部屬性[[Description]]
中, 當(dāng)符號的toString()
方法被顯式或隱式調(diào)用時, 該屬性都會被讀取. 此外沒有任何辦法可以從代碼中直接訪問[[Description]]
屬性
let firstName = Symbol("first name");
let person = {};
person[firstName] = "Nicholas";
console.log("first name" in person); // false
console.log(person[firstName]); // "Nicholas"
console.log(firstName); // "Symbol(first name)"
-
識別符號值: 可以使用
typeof
運算符來判斷一個變量是否為符號let symbol = Symbol("test symbol"); console.log(typeof symbol); // "symbol"
盡管有其他方法可以判斷一個變量是否為符號蓄喇, typeof 運算符依然是最準確、最優(yōu)先
的判別手段乍炉。
使用符號值
可以在任意能使用 "需計算屬性名" 的場合使用符號. 還可以在 Object.defineProperty()
或 Object.defineProperties()
調(diào)用中使用它
let firstName = Symbol("first name");
// 使用一個需計算字面量屬性
let person = {
[firstName]: "Nicholas"
};
// 讓該屬性變?yōu)橹蛔x
Object.defineProperty(person, firstName, { writable: false });
let lastName = Symbol("last name");
Object.defineProperties(person, {
[lastName]: {
value: "Zakas",
writable: false
}
});
console.log(person[firstName]); // "Nicholas"
console.log(person[lastName]); // "Zakas"
共享符號值
-
創(chuàng)建共享符號值, 應(yīng)使用
Symbol.for()
方法而不是Symbol()
方法.Symbol.for()
方法僅接受單個字符串類型的參數(shù)根欧,作為目標符號值的標識符,同時此參數(shù)也會成為該符號的描述信息瞬测。let uid = Symbol.for("uid"); let object = {}; object[uid] = "12345"; console.log(object[uid]); // "12345" console.log(uid); // "Symbol(uid)"
Symbol.for()
方法首先會搜索全局符號注冊表,看是否存在一個鍵值為 "uid" 的符號值纠炮。若是月趟,該方法會返回這個已存在的符號值;否則恢口,會創(chuàng)建一個新的符號值孝宗,并使用該鍵值將其記錄到全局符號注冊表中,然后返回這個新的符號值let uid = Symbol.for("uid"); let object = { [uid]: "12345" }; console.log(object[uid]); // "12345" console.log(uid); // "Symbol(uid)" let uid2 = Symbol.for("uid"); console.log(uid === uid2); // true console.log(object[uid2]); // "12345" console.log(uid2); // "Symbol(uid)"
-
可以使用
Symbol.keyFor()
方法在全局符號注冊表中根據(jù)符號值檢索出對應(yīng)的鍵值,注意: 使用
Symobol()
方法創(chuàng)建的是不會注冊到全局符號注冊表的let uid = Symbol.for("uid"); console.log(Symbol.keyFor(uid)); // "uid" let uid2 = Symbol.for("uid"); console.log(Symbol.keyFor(uid2)); // "uid" let uid3 = Symbol("uid"); console.log(Symbol.keyFor(uid3)); // undefined
全局符號注冊表類似于全局作用域耕肩,是一個共享環(huán)境因妇,這意味著你不應(yīng)當(dāng)假設(shè)某些值是否已存在于其中问潭。在使用第三方組件時,為符號的鍵值使用命名空間能夠減少命名沖突的可能性婚被,舉個例子: jQuery 代碼應(yīng)當(dāng)為它的所有鍵值使用 "jquery." 的前綴狡忙,如"jquery.element" 或類似的形式。
符號值的轉(zhuǎn)換
- 符號類型在進行轉(zhuǎn)換時非常不靈活, 因為其他類型缺乏與符號值的合理等價, 尤其是符號值無法被轉(zhuǎn)換為字符串值或數(shù)值(會拋出錯誤)
- 無論對符號使用哪種數(shù)學(xué)運算符都會導(dǎo)致錯誤址芯,但使用邏輯運算符則不會去枷,因為符號值在邏輯運算中會被認為等價于 true (就像 JS中其他的非空值那樣)。
檢索符號屬性
ES6 新增了Object.getOwnPropertySymbols() 方法是复,以便讓你可以檢索對象的符號類型屬性删顶。
let uid = Symbol.for("uid");
let object = {
[uid]: "12345"
};
let symbols = Object.getOwnPropertySymbols(object);
console.log(symbols.length); // 1
console.log(symbols[0]); // "Symbol(uid)"
console.log(object[symbols[0]]); // "12345"
使用知名符號暴露內(nèi)部方法
ES5 的中心主題之一是披露并定義了一些魔術(shù)般的成分,而這些部分是當(dāng)時開發(fā)者所無法自行模擬的淑廊。 ES6 延續(xù)了這些工作逗余,對原先屬于語言內(nèi)部邏輯的部分進行了進一步的暴露,允許使用符號類型的原型屬性來定義某些對象的基礎(chǔ)行為季惩。
**這些符號是: **
- Symbol.hasInstance :供
instanceof
運算符使用的一個方法录粱,用于判斷對象繼承關(guān)系。 - Symbol.isConcatSpreadable :一個布爾類型值画拾,在集合對象作為參數(shù)傳遞給Array.prototype.concat() 方法時啥繁,指示是否要將該集合的元素扁平化。
- Symbol.iterator :返回迭代器(參閱第七章)的一個方法青抛。
- Symbol.match :供 String.prototype.match() 函數(shù)使用的一個方法旗闽,用于比較字符串。
- Symbol.replace :供 String.prototype.replace() 函數(shù)使用的一個方法蜜另,用于替換子字符串适室。
- Symbol.search :供 String.prototype.search() 函數(shù)使用的一個方法,用于定位子字符串
- Symbol.species :用于產(chǎn)生派生對象(參閱第八章)的構(gòu)造器举瑰。
- Symbol.split :供 String.prototype.split() 函數(shù)使用的一個方法捣辆,用于分割字符串。
- Symbol.toPrimitive :返回對象所對應(yīng)的基本類型值的一個方法此迅。
- Symbol.toStringTag :供 String.prototype.toString() 函數(shù)使用的一個方法汽畴,用于創(chuàng)建對象的描述信息。
- Symbol.unscopables :一個對象耸序,該對象的屬性指示了哪些屬性名不允許被包含在with 語句中忍些。
重寫知名符號所定義的方法, 會把一個普通對象改變成奇異對象, 因為它改變了一些默認的內(nèi)部行為
1. Symbol.hasInstance 屬性
-
作用: 用于判斷指定對象是否為本函數(shù)的一個實例。 方法定義在
Function.prototype
上, 因此所有函數(shù)都繼承了面對instanceof
運算符時的默認行為obj instanceof Array; // 等價于 -- ES6 從本質(zhì)上將 instanceof 運算符重定義為上述方法調(diào)用的簡寫語法佑吝,這樣使用 instanceof 便會觸發(fā)一次方法調(diào)用坐昙,實際上允許你改變該運算符的工作绳匀。 Array[Symbol.hasInstance](obj);
Symbol.hasInstance
屬性自身是不可寫入芋忿、不可配置炸客、不可枚舉的,從而保證它不會被錯誤地重寫-
要重寫一個不可寫入的屬性戈钢, 必須使用
Object.defineProperty()
function SpecialNumber() { // empty } // 通過重寫構(gòu)造函數(shù)本身的屬性來重寫 instanceof 運算符的結(jié)果 Object.defineProperty(SpecialNumber, Symbol.hasInstance, { value: function(v) { // 將介于 1 到 100 之間的數(shù)值認定為一個特殊的數(shù)值類型痹仙, return (v instanceof Number) && (v >=1 && v <= 100); } }); let two = new Number(2), zero = new Number(0); console.log(two instanceof SpecialNumber); // true console.log(zero instanceof SpecialNumber); // false
可以重寫所有內(nèi)置函數(shù)(例如 Date 或 Error )的 Symbol.hasInstance 屬性,但并不建議這么做殉了,因為這會讓你的代碼變得難以預(yù)測而又混亂开仰。最好僅在必要時對自己的函數(shù)重寫Symbol.hasInstance 。
2. Symbol.isConcatSpreadable
- 作用: Symbol.isConcatSpreadable 屬性是一個布爾類型的屬性薪铜,它表示目標對象擁有長度屬性與數(shù)值類型的鍵众弓、并且數(shù)值類型鍵所對應(yīng)的屬性值在參與 concat() 調(diào)用時需要被分離為個體。
3. Symbol.match隔箍、 Symbol.replace谓娃、Symbol.search、Symobol.split
- 作用: 這 4 個符號表示可以將正則表達式作為字符串對應(yīng)方法的第一個參數(shù)傳入蜒滩, Symbol.match對應(yīng) match() 方法滨达, Symbol.replace 對應(yīng) replace() , Symbol.search 對應(yīng) search()俯艰, Symbol.split 則對應(yīng) split() 捡遍。這些符號屬性被定義在 RegExp.prototype 上作為默認實現(xiàn),以供對應(yīng)的字符串方法使用竹握。
4. Symbol.toPrimittive
-
作用: Symbol.toPrimitive 方法被定義在所有常規(guī)類型的原型上画株,規(guī)定了在對象被轉(zhuǎn)換為基本類型值的時候會發(fā)生什么。當(dāng)需要轉(zhuǎn)換時啦辐, Symbol.toPrimitive 會被調(diào)用污秆,并按照規(guī)范傳入一個提示性字符串參數(shù)。該參數(shù)有 3 種可能:當(dāng)參數(shù)值為 "number" 的時候昧甘,Symbol.toPrimitive 應(yīng)當(dāng)返回一個數(shù)值良拼;當(dāng)參數(shù)值為 "string" 的時候,應(yīng)當(dāng)返回一個字符串充边;而當(dāng)參數(shù)為 "default" 的時候庸推,對返回值類型沒有特別要求。
通過定義 Symbol.toPrimitive 方法浇冰,你可以重寫這些默認的轉(zhuǎn)換行為贬媒。
對于大部分常規(guī)對象,“數(shù)值模式”依次會有下述行為:
- 調(diào)用 valueOf() 方法肘习,如果方法返回值是一個基本類型值际乘,那么返回它;
- 否則漂佩,調(diào)用 toString() 方法脖含,如果方法返回值是一個基本類型值罪塔,那么返回它;
- 否則养葵,拋出一個錯誤征堪。
類似的,對于大部分常規(guī)對象关拒,“字符串模式”依次會有下述行為:
調(diào)用 toString() 方法佃蚜,如果方法返回值是一個基本類型值,那么返回它着绊;
否則谐算,調(diào)用 valueOf() 方法,如果方法返回值是一個基本類型值归露,那么返回它氯夷;
否則,拋出一個錯誤
在多數(shù)情況下靶擦,常規(guī)對象的默認模式都等價于數(shù)值模式(只有 Date 類型例外腮考,它默認使用
字符串模式) ==> “默認模式”只在使用 == 運算符、 + 運算符玄捕、或者傳遞單一參數(shù)給 Date 構(gòu)造器的時候被使用踩蔚,而大部分運算符都使用字符串模式或是數(shù)值模式
function Temperature(degrees) {
this.degrees = degrees;
}
Temperature.prototype[Symbol.toPrimitive] = function(hint) {
switch (hint) {
case "string":
return this.degrees + "\u00b0"; // 溫度符號
case "number":
return this.degrees;
case "default":
return this.degrees + " degrees";
}
};
let freezing = new Temperature(32);
console.log(freezing + "!"); // "32 degrees!"
console.log(freezing / 2); // 16
console.log(String(freezing)); // "32°"
5. Symbol.toStringTag
- 作用: ES6 通過 Symbol.toStringTag 重定義了相關(guān)行為,該符號代表了所有對象的一個屬性枚粘,定義了 Object.prototype.toString.call() 被調(diào)用時應(yīng)當(dāng)返回什么值馅闽。也就是修改對象內(nèi)部屬性[[class]]
function Person(name) {
this.name = name;
}
Person.prototype[Symbol.toStringTag] = "Person";
let me = new Person("Nicholas");
console.log(me.toString()); // "[object Person]"
console.log(Object.prototype.toString.call(me)); // "[object Person]"
6. Symbol.unscopables
- 作用: 在 Array.prototype 上使用,以指定哪些屬性不允許在 with 語句內(nèi)被綁定馍迄。