原創(chuàng)文章&經(jīng)驗(yàn)總結(jié)&從校招到A廠一路陽光一路滄桑
詳情請(qǐng)戳www.codercc.com
主要知識(shí)點(diǎn):創(chuàng)建符號(hào)值退疫、使用符號(hào)值、共享符號(hào)值鸽素、符號(hào)值轉(zhuǎn)換褒繁。檢索符號(hào)值屬性以及知名符號(hào)
1. Symbol基礎(chǔ)
1.1 創(chuàng)建符號(hào)值
在 JS 已有的基本類型(字符串、數(shù)值馍忽、布爾類型棒坏、 null
與 undefined
) 之外燕差, ES6 引入了一種新的基本類型:符號(hào)(Symbol
) 。 符號(hào)起初被設(shè)計(jì)用于創(chuàng)建對(duì)象私有成員坝冕,而這也是 JS 開發(fā)者期待已久的特性徒探。在符號(hào)誕生之前,將字符串作為屬性名稱導(dǎo)致屬性可以被輕易訪問喂窟,無論命名規(guī)則如何测暗。而“私有名稱”意味著開發(fā)者可以創(chuàng)建非字符串類型的屬性名稱,由此可以防止使用常規(guī)手段來探查這些名稱磨澡。
創(chuàng)建符號(hào)值
let firstName = Symbol('first Name');
let person = {};
person[firstName] = 'hello world';
console.log(person[firstName]); //hello world
console.log(firstName); //Symbol(first Name)
此代碼創(chuàng)建了一個(gè)符號(hào)類型的 firstName
變量碗啄,并將它作為 person
對(duì)象的一個(gè)屬性,而每次訪問該屬性都要使用這個(gè)符號(hào)值稳摄。Symbol
函數(shù)還可以接受一個(gè)額外的參數(shù)用于描述符號(hào)值稚字,該描述并不能用來訪問對(duì)應(yīng)屬性,但它能用于調(diào)試厦酬。符號(hào)的描述信息被存儲(chǔ)在內(nèi)部屬性 [[Description]]
中胆描,當(dāng)符號(hào)的 toString()
方法被顯式
或隱式調(diào)用時(shí),該屬性都會(huì)被讀取仗阅。在本例中昌讲, console.log()
隱式調(diào)用了 firstName
變量的 toString()
方法,于是描述信息就被輸出到日志减噪。此外沒有任何辦法可以從代碼中直接訪問 [[Description]]
屬性剧蚣。
1.2 使用符號(hào)值
可以在任意使用 需計(jì)算屬性名 的場(chǎng)合中使用符號(hào)值,比如在對(duì)象字面量中可以使用符號(hào)值來作為對(duì)象屬性旋廷,另外還可以在Object.defineProperty()
方法或Object.defineProperties()
方法中使用鸠按。
1.3 共享符號(hào)值
如果想在不同的代碼中共享同一個(gè)符號(hào)值的話,應(yīng)使用 Symbol.for()
方法而不是 Symbol()
方法饶碘。 Symbol.for()
方法僅接受單個(gè)字符串類型的參數(shù)目尖,作為目標(biāo)符號(hào)值的標(biāo)識(shí)符,同時(shí)此參數(shù)也會(huì)成為該符號(hào)的描述信息扎运。
//共享符號(hào)值
let uid = Symbol.for('uid');
let person = {};
person[uid] = 'hello';
let id = Symbol.for('uid');
let car = {};
car[id] = '88888888';
console.log(uid===id); //true
Symbol.for()
方法首先會(huì)搜索全局符號(hào)注冊(cè)表瑟曲,看是否存在一個(gè)鍵值為 "uid
" 的符號(hào)值。
若是豪治,該方法會(huì)返回這個(gè)已存在的符號(hào)值洞拨;否則,會(huì)創(chuàng)建一個(gè)新的符號(hào)值负拟,并使用該鍵值將
其記錄到全局符號(hào)注冊(cè)表中烦衣,然后返回這個(gè)新的符號(hào)值。這就意味著此后使用同一個(gè)鍵值去調(diào)用 Symbol.for()
方法都將會(huì)返回同一個(gè)符號(hào)值。
還可以使用 Symbol.keyFor()
方法在全局符號(hào)注冊(cè)表中根據(jù)符號(hào)值檢索出對(duì)應(yīng)的鍵值:
console.log(Symbol.keyFor(uid)); //uid
console.log(Symbol.keyFor(id)); //uid
1.4 符號(hào)值的轉(zhuǎn)換
類型轉(zhuǎn)換是 JS 語言重要的一部分花吟,能夠非常靈活地將一種數(shù)據(jù)類型轉(zhuǎn)換為另一種秸歧。然而符號(hào)類型在進(jìn)行轉(zhuǎn)換時(shí)非常不靈活,因?yàn)槠渌愋腿狈εc符號(hào)值的合理等價(jià)衅澈,尤其是符號(hào)值無法被轉(zhuǎn)換為字符串值或數(shù)值键菱,在邏輯運(yùn)算符中會(huì)被認(rèn)為等價(jià)于 true
。
例如今布,可使用String()方法獲取符號(hào)值的描述信息经备,但是如果使用符號(hào)值在字符串中拼接,則會(huì)報(bào)錯(cuò):
console.log(String(uid));//Symbol(uid)
console.log(uid+""); //Uncaught TypeError: Cannot convert a Symbol value to a string
1.5 檢索符號(hào)屬性
Object.keys()
與 Object.getOwnPropertyNames()
方法可以檢索對(duì)象的所有屬性名稱部默,前者
返回所有的可枚舉屬性名稱侵蒙,而后者則返回所有屬性名稱,無論是否可以可枚舉甩牺,然而兩者都不能返回符號(hào)類型的屬性蘑志。因此累奈,在ES6中新增了
Object.getOwnPropertySymbols()
方法贬派,以便讓你可以檢索對(duì)象的符號(hào)類型屬性。
let uid = Symbol.for('uid');
let person = {};
person[uid] = 'hello';
let symbols = Object.getOwnPropertySymbols(person);
console.log(symbols.length); //1
console.log(symbols[0]); //Symbol(uid)
2. 知名符號(hào)
ES6 定義了“知名符號(hào)”來代表 JS 中一些公共行為澎媒,而這些行為此前被認(rèn)為只能是內(nèi)部操作搞乏,但在ES6中使用了知名符號(hào)來暴露了內(nèi)部方法,提供了更加方便的調(diào)用這種公用方法的一種方式戒努。每一個(gè)知名符號(hào)都對(duì)應(yīng)全局 Symbol
對(duì)象的一個(gè)屬性请敦,例如 Symbol.create
。
知名符號(hào)有:
- Symbol.hasInstance :供 instanceof 運(yùn)算符使用的一個(gè)方法储玫,用于判斷對(duì)象繼承關(guān)系侍筛;
-
Symbol.isConcatSpreadable :一個(gè)布爾類型值,在集合對(duì)象作為參數(shù)傳遞給
Array.prototype.concat() 方法時(shí)撒穷,指示是否要將該集合的元素扁平化匣椰; - Symbol.iterator :返回迭代器的一個(gè)方法;
- Symbol.match :供 String.prototype.match() 函數(shù)使用的一個(gè)方法端礼,用于比較字符串禽笑;
- Symbol.replace :供 String.prototype.replace() 函數(shù)使用的一個(gè)方法,用于替換子字符串蛤奥;
- Symbol.search :供 String.prototype.search() 函數(shù)使用的一個(gè)方法佳镜,用于定位子字符;
- Symbol.species :用于產(chǎn)生派生對(duì)象的構(gòu)造器凡桥;
- Symbol.split :供 String.prototype.split() 函數(shù)使用的一個(gè)方法蟀伸,用于分割字符串;
- Symbol.toPrimitive :返回對(duì)象所對(duì)應(yīng)的基本類型值的一個(gè)方法;
- Symbol.toStringTag :供 String.prototype.toString() 函數(shù)使用的一個(gè)方法望蜡,用于創(chuàng)建對(duì)象的描述信息唤崭;
- Symbol.unscopables :一個(gè)對(duì)象,該對(duì)象的屬性指示了哪些屬性名不允許被包含在with 語句中脖律;
2.1 Symbol.hasInstance屬性
每個(gè)函數(shù)都具有一個(gè) Symbol.hasInstance
屬性谢肾,用于判斷指定對(duì)象是否為本函數(shù)的一個(gè)實(shí)例。這個(gè)方法定義在 Function.prototype
上小泉,因此所有函數(shù)都繼承了面對(duì) instanceof
運(yùn)算符時(shí)的默認(rèn)行為芦疏。 Symbol.hasInstance
屬性自身是不可寫入、不可配置微姊、不可枚舉的酸茴,從而保證它不會(huì)被錯(cuò)誤地重寫。
Symbol.hasInstance
方法只接受單個(gè)參數(shù)兢交,即需要檢測(cè)的值薪捍。如果該值是本函數(shù)的一個(gè)實(shí)例,則方法會(huì)返回 true
obj instanceof Array
//等價(jià)于
Array[Symbol.hasInstance](obj)
2.2 Symbol.isConcatSpreadable屬性
Symbol.isConcatSpreadable
屬性是一個(gè)布爾類型的屬性配喳,它表示擁有長(zhǎng)度length
屬性與數(shù)值類型的鍵的類數(shù)組的對(duì)象酪穿,在調(diào)用 concat()
方法時(shí),會(huì)將數(shù)值鍵對(duì)應(yīng)的值分離為單獨(dú)項(xiàng)晴裹,添加在數(shù)組的后部被济,完成數(shù)組拼接。
它只出現(xiàn)在特定類型的對(duì)象上涧团,用來標(biāo)示該對(duì)象在作為concat()
參數(shù)時(shí)應(yīng)如何工作只磷,從而有效改變?cè)搶?duì)象的默認(rèn)行為。你可以用它來定義任意類型的對(duì)象泌绣,讓該對(duì)象在參與 concat()
調(diào)用時(shí)能夠表現(xiàn)得像數(shù)組一樣钮追。
//Symbol.isConcatSpreadable屬性
let collection = {
0:'hello',
1:'world',
length:2,
[Symbol.isConcatSpreadable]:true
}
let arr = ['es6'].concat(collection);
console.log(arr); //["es6", "hello", "world"]
2.3 Symbol.match 、 Symbol.replace 阿迈、 Symbol.search與Symbol.split
在 JS 中元媚,字符串與正則表達(dá)式有著密切的聯(lián)系,尤其是字符串具有幾個(gè)可以接受正則表達(dá)式作為參數(shù)的方法:
- match(regex) :判斷指定字符串是否與一個(gè)正則表達(dá)式相匹配仿滔;
- replace(regex, replacement) :對(duì)正則表達(dá)式的匹配結(jié)果進(jìn)行替換惠毁;
- search(regex) :在字符串內(nèi)對(duì)正則表達(dá)式的匹配結(jié)果進(jìn)行定位;
- split(regex) :使用正則表達(dá)式將字符串分割為數(shù)組
ES6 定義了 4 個(gè)符號(hào)以及對(duì)應(yīng)的方法崎页,可以將正則表達(dá)式作為字符串對(duì)應(yīng)方法的第一個(gè)參數(shù)傳入鞠绰, Symbol.match
對(duì)應(yīng) match()
方法, Symbol.replace
對(duì)應(yīng) replace()
飒焦,Symbol.search
對(duì)應(yīng) search()
蜈膨, Symbol.split
則對(duì)應(yīng) split()
屿笼。這些符號(hào)屬性被定義在 RegExp.prototype
上作為默認(rèn)實(shí)現(xiàn),以供對(duì)應(yīng)的字符串方法使用翁巍。
2.4 Symbol.toPrimitive
JS 經(jīng)常在使用特定運(yùn)算符的時(shí)候試圖進(jìn)行隱式轉(zhuǎn)換驴一,以便將對(duì)象轉(zhuǎn)換為基本類型值。例如灶壶,當(dāng)你使用相等(==
) 運(yùn)算符來對(duì)字符串與對(duì)象進(jìn)行比較的時(shí)候肝断,該對(duì)象會(huì)在比較之前被轉(zhuǎn)換為一個(gè)基本類型值。到底轉(zhuǎn)換為什么基本類型值驰凛,在此前屬于內(nèi)部操作胸懈,而 ES6 則通過Symbol.toPrimitive
屬性將其暴露出來,以便讓對(duì)應(yīng)方法可以被修改恰响。
function Temperature(degrees) {
this.degrees = degrees;
}
Temperature.prototype[Symbol.toPrimitive] = function(hint) {
switch (hint) {
case "string":
return this.degrees + "\u00b0"; // 溫度符號(hào)
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°"
這段腳本定義了一個(gè) Temperature 構(gòu)造器趣钱,并重寫了其原型上的Symbol.toPrimitive
方法。返回值會(huì)依據(jù)方法的提示性參數(shù)而有所不同胚宦,可以使用字符串模式首有、數(shù)值模式或是默認(rèn)模式,而該提示性參數(shù)會(huì)在調(diào)用時(shí)由 JS 引擎自動(dòng)填寫枢劝。字符串模式中井联, Temperature 函數(shù)返回的溫度會(huì)附帶著 Unicode 溫度符號(hào);數(shù)值模式只會(huì)返回溫度數(shù)值呈野;而默認(rèn)模式中低矮,返回的溫度會(huì)附帶著字符串 "degrees" 印叁。
2.5 Symbol.toStringTag
ES6 通過 Symbol.toStringTag
重定義了相關(guān)行為被冒,該符號(hào)代表了所有對(duì)象的一個(gè)屬性,定義了 Object.prototype.toString.call()
被調(diào)用時(shí)應(yīng)當(dāng)返回什么值轮蜕。對(duì)于數(shù)組來說昨悼,在Symbol.toStringTag
屬性中存儲(chǔ)了 "Array" 值,于是該函數(shù)的返回值也就是 "Array" 跃洛。
function Person(name){
this.name = name;
}
Person.prototype[Symbol.toStringTag] = 'person';
let person = new Person('hello world');
console.log(person.toString()); //[object person]
2.6 Symbol.unscopables
Symbol.unscopables
符號(hào)在 Array.prototype
上使用率触,以指定哪些屬性不允許在 with
語句內(nèi)被綁定。 Symbol.unscopables
屬性是一個(gè)對(duì)象汇竭,當(dāng)提供該屬性時(shí)葱蝗,它的鍵就是用于忽略with
語句綁定的標(biāo)識(shí)符,鍵值為 true
代表屏蔽綁定细燎。以下是數(shù)組的 Symbol.unscopables
屬性的默認(rèn)值:
// 默認(rèn)內(nèi)置在 ES6 中
Array.prototype[Symbol.unscopables] = Object.assign(Object.create(null), {
copyWithin: true,
entries: true,
fill: true,
find: true,
findIndex: true,
keys: true,
values: true
});
3. 總結(jié)
- 雖然符號(hào)類型的屬性不是真正的私有屬性两曼,但它們難以被無意修改,因此在需要提供保護(hù)以防止開發(fā)者改動(dòng)的場(chǎng)合中玻驻,它們非常合適悼凑;
- 為符號(hào)提供描述信息以便更容易地辨識(shí)它們的值。當(dāng)需要在不同代碼片段中共享符號(hào),可以是用S
ymbol.for()
在全局符號(hào)注冊(cè)表中共享符號(hào)户辫; -
Object.keys()
或Object.getOwnPropertyNames()
不會(huì)返回符號(hào)值渐夸,因此 ES6 新增了一個(gè)Object.getOwnPropertySymbols()
方法,允許檢索符號(hào)類型的對(duì)象屬性渔欢。同時(shí)依然可以使用Object.defineProperty()
與Object.defineProperties()
方法對(duì)符號(hào)類型的屬性進(jìn)行修改墓塌; - “知名符號(hào)”使用了全局符號(hào)常量(例如
Symbol.hasInstance
) ,為常規(guī)對(duì)象定義了一些功能奥额,而這些功能原先僅限內(nèi)部使用桃纯。這些符號(hào)按規(guī)范使用Symbol.
的前綴,允許開發(fā)者通過多種方式去修改常規(guī)對(duì)象的行為披坏。