深入理解ES6--6.符號(hào)與符號(hào)屬性

原創(chuàng)文章&經(jīng)驗(yàn)總結(jié)&從校招到A廠一路陽光一路滄桑

詳情請(qǐng)戳www.codercc.com

image

主要知識(shí)點(diǎn):創(chuàng)建符號(hào)值退疫、使用符號(hào)值、共享符號(hào)值鸽素、符號(hào)值轉(zhuǎn)換褒繁。檢索符號(hào)值屬性以及知名符號(hào)

符號(hào)與符號(hào)屬性的知識(shí)點(diǎn).png

1. Symbol基礎(chǔ)

1.1 創(chuàng)建符號(hào)值

在 JS 已有的基本類型(字符串、數(shù)值馍忽、布爾類型棒坏、 nullundefined ) 之外燕差, 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é)

  1. 雖然符號(hào)類型的屬性不是真正的私有屬性两曼,但它們難以被無意修改,因此在需要提供保護(hù)以防止開發(fā)者改動(dòng)的場(chǎng)合中玻驻,它們非常合適悼凑;
  2. 為符號(hào)提供描述信息以便更容易地辨識(shí)它們的值。當(dāng)需要在不同代碼片段中共享符號(hào),可以是用Symbol.for()在全局符號(hào)注冊(cè)表中共享符號(hào)户辫;
  3. Object.keys()Object.getOwnPropertyNames() 不會(huì)返回符號(hào)值渐夸,因此 ES6 新增了一個(gè)Object.getOwnPropertySymbols()方法,允許檢索符號(hào)類型的對(duì)象屬性渔欢。同時(shí)依然可以使用Object.defineProperty()Object.defineProperties() 方法對(duì)符號(hào)類型的屬性進(jìn)行修改墓塌;
  4. “知名符號(hào)”使用了全局符號(hào)常量(例如 Symbol.hasInstance ) ,為常規(guī)對(duì)象定義了一些功能奥额,而這些功能原先僅限內(nèi)部使用桃纯。這些符號(hào)按規(guī)范使用 Symbol. 的前綴,允許開發(fā)者通過多種方式去修改常規(guī)對(duì)象的行為披坏。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末态坦,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子棒拂,更是在濱河造成了極大的恐慌伞梯,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件帚屉,死亡現(xiàn)場(chǎng)離奇詭異谜诫,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)攻旦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門喻旷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人牢屋,你說我怎么就攤上這事且预。” “怎么了烙无?”我有些...
    開封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵锋谐,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我截酷,道長(zhǎng)涮拗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任迂苛,我火速辦了婚禮三热,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘三幻。我一直安慰自己,他們只是感情好催跪,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開白布骑丸。 她就那樣靜靜地躺著灌曙,像睡著了一般在刺。 火紅的嫁衣襯著肌膚如雪魄幕。 梳的紋絲不亂的頭發(fā)上留储,一...
    開封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天赔嚎,我揣著相機(jī)與錄音结缚,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛赌朋,可吹牛的內(nèi)容都是我干的篇裁。 我是一名探鬼主播沛慢,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼达布!你這毒婦竟也來了团甲?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤黍聂,失蹤者是張志新(化名)和其女友劉穎伐庭,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體分冈,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡圾另,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了雕沉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片集乔。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖坡椒,靈堂內(nèi)的尸體忽然破棺而出扰路,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站萨醒,受9級(jí)特大地震影響祷杈,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜冠骄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧际插,春花似錦、人聲如沸显设。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捕捂。三九已至瑟枫,卻和暖如春斗搞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背慷妙。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工榜旦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人景殷。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓溅呢,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親猿挚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子咐旧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容