ES6學(xué)習(xí)筆記7-symbol

概述

ES5 的對(duì)象屬性名都是字符串,這容易造成屬性名的沖突。比如,你使用了一個(gè)他人提供的對(duì)象践图,但又想為這個(gè)對(duì)象添加新的方法(mixin 模式),新方法的名字就有可能與現(xiàn)有方法產(chǎn)生沖突沉馆。如果有一種機(jī)制码党,保證每個(gè)屬性的名字都是獨(dú)一無二的就好了,這樣就從根本上防止屬性名的沖突斥黑。這就是 ES6 引入Symbol的原因揖盘。

Symbol 值通過Symbol函數(shù)生成。這就是說锌奴,對(duì)象的屬性名現(xiàn)在可以有兩種類型兽狭,一種是原來就有的字符串,另一種就是新增的 Symbol 類型鹿蜀。凡是屬性名屬于 Symbol 類型箕慧,就都是獨(dú)一無二的,可以保證不會(huì)與其他屬性名產(chǎn)生沖突耻姥。

注意销钝,Symbol函數(shù)前不能使用new命令有咨,否則會(huì)報(bào)錯(cuò)琐簇。這是因?yàn)樯傻?Symbol 是一個(gè)原始類型的值,不是對(duì)象座享。也就是說婉商,由于 Symbol 值不是對(duì)象,所以不能添加屬性渣叛≌芍龋基本上,它是一種類似于字符串的數(shù)據(jù)類型淳衙。

Symbol函數(shù)可以接受一個(gè)字符串作為參數(shù)蘑秽,表示對(duì) Symbol 實(shí)例的描述饺著,主要是為了在控制臺(tái)顯示,或者轉(zhuǎn)為字符串時(shí)肠牲,比較容易區(qū)分幼衰。

let s1 = Symbol('foo');
let s2 = Symbol('bar');

s1 // Symbol(foo)
s2 // Symbol(bar)

s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"

如果 Symbol 的參數(shù)是一個(gè)對(duì)象,就會(huì)調(diào)用該對(duì)象的toString方法缀雳,將其轉(zhuǎn)為字符串渡嚣,然后才生成一個(gè) Symbol 值。

注意肥印,Symbol函數(shù)的參數(shù)只是表示對(duì)當(dāng)前 Symbol 值的描述识椰,因此相同參數(shù)的Symbol函數(shù)的返回值是不相等的。

Symbol 值不能與其他類型的值進(jìn)行運(yùn)算深碱,會(huì)報(bào)錯(cuò)腹鹉。但是,Symbol 值可以顯式轉(zhuǎn)為字符串敷硅。另外种蘸,Symbol 值也可以轉(zhuǎn)為布爾值,但是不能轉(zhuǎn)為數(shù)值竞膳。

作為屬性名的 Symbol

由于每一個(gè) Symbol 值都是不相等的航瞭,這意味著 Symbol 值可以作為標(biāo)識(shí)符,用于對(duì)象的屬性名坦辟,就能保證不會(huì)出現(xiàn)同名的屬性刊侯。這對(duì)于一個(gè)對(duì)象由多個(gè)模塊構(gòu)成的情況非常有用,能防止某一個(gè)鍵被不小心改寫或覆蓋锉走。

注意滨彻,Symbol 值作為對(duì)象屬性名時(shí),不能用點(diǎn)運(yùn)算符挪蹭。

const mySymbol = Symbol();
const a = {};

a.mySymbol = 'Hello!';
a[mySymbol] // undefined
a['mySymbol'] // "Hello!"

上面代碼中亭饵,因?yàn)辄c(diǎn)運(yùn)算符后面總是字符串,所以不會(huì)讀取mySymbol作為標(biāo)識(shí)名所指代的那個(gè)值梁厉,導(dǎo)致a的屬性名實(shí)際上是一個(gè)字符串辜羊,而不是一個(gè) Symbol 值。

Symbol 類型還可以用于定義一組常量词顾,保證這組常量的值都是不相等的八秃。

還有一點(diǎn)需要注意,Symbol 值作為屬性名時(shí)肉盹,該屬性還是公開屬性昔驱,不是私有屬性。

實(shí)例:消除魔術(shù)字符串

魔術(shù)字符串指的是上忍,在代碼之中多次出現(xiàn)骤肛、與代碼形成強(qiáng)耦合的某一個(gè)具體的字符串或者數(shù)值纳本。風(fēng)格良好的代碼,應(yīng)該盡量消除魔術(shù)字符串腋颠,改由含義清晰的變量代替饮醇。

function getArea(shape, options) {
  let area = 0;

  switch (shape) {
    case 'Triangle': // 魔術(shù)字符串
      area = .5 * options.width * options.height;
      break;
    /* ... more code ... */
  }

  return area;
}

getArea('Triangle', { width: 100, height: 100 }); // 魔術(shù)字符串

上面代碼中,字符串Triangle就是一個(gè)魔術(shù)字符串秕豫。它多次出現(xiàn)朴艰,與代碼形成“強(qiáng)耦合”,不利于將來的修改和維護(hù)混移。

常用的消除魔術(shù)字符串的方法祠墅,就是把它寫成一個(gè)變量。

const shapeType = {
  triangle: 'Triangle'
};

function getArea(shape, options) {
  let area = 0;
  switch (shape) {
    case shapeType.triangle:
      area = .5 * options.width * options.height;
      break;
  }
  return area;
}

getArea(shapeType.triangle, { width: 100, height: 100 });

上面代碼中歌径,我們把Triangle寫成shapeType對(duì)象的triangle屬性毁嗦,這樣就消除了強(qiáng)耦合。

如果仔細(xì)分析回铛,可以發(fā)現(xiàn)shapeType.triangle等于哪個(gè)值并不重要狗准,只要確保不會(huì)跟其他shapeType屬性的值沖突即可。因此茵肃,這里就很適合改用 Symbol 值腔长。

const shapeType = {
  triangle: Symbol()
};

屬性名的遍歷

Symbol 作為屬性名,該屬性不會(huì)出現(xiàn)在for...in验残、for...of循環(huán)中捞附,也不會(huì)被Object.keys()、Object.getOwnPropertyNames()您没、JSON.stringify()返回鸟召。但是,它也不是私有屬性氨鹏,有一個(gè)Object.getOwnPropertySymbols方法欧募,可以獲取指定對(duì)象的所有 Symbol 屬性名。

另一個(gè)新的 API仆抵,Reflect.ownKeys方法可以返回所有類型的鍵名跟继,包括常規(guī)鍵名和 Symbol 鍵名。

由于以 Symbol 值作為名稱的屬性肢础,不會(huì)被常規(guī)方法遍歷得到还栓。我們可以利用這個(gè)特性,為對(duì)象定義一些非私有的传轰、但又希望只用于內(nèi)部的方法。
let size = Symbol('size');
如一個(gè)數(shù)組的大小谷婆。

Symbol.for()慨蛙,Symbol.keyFor()

有時(shí)辽聊,我們希望重新使用同一個(gè) Symbol 值,Symbol.for方法可以做到這一點(diǎn)期贫。它接受一個(gè)字符串作為參數(shù)跟匆,然后搜索有沒有以該參數(shù)作為名稱的 Symbol 值。如果有通砍,就返回這個(gè) Symbol 值玛臂,否則就新建并返回一個(gè)以該字符串為名稱的 Symbol 值。

比如封孙,如果你調(diào)用Symbol.for("cat")30 次迹冤,每次都會(huì)返回同一個(gè) Symbol 值,但是調(diào)用Symbol("cat")30 次虎忌,會(huì)返回 30 個(gè)不同的 Symbol 值泡徙。

Symbol.keyFor方法返回一個(gè)已登記的 Symbol 類型值的key。
需要注意的是膜蠢,Symbol.for為 Symbol 值登記的名字堪藐,是全局環(huán)境的,可以在不同的 iframe 或 service worker 中取到同一個(gè)值挑围。

實(shí)例:模塊的 Singleton 模式

Singleton 模式指的是調(diào)用一個(gè)類礁竞,任何時(shí)候返回的都是同一個(gè)實(shí)例。

對(duì)于 Node 來說杉辙,模塊文件可以看成是一個(gè)類苏章。怎么保證每次執(zhí)行這個(gè)模塊文件,返回的都是同一個(gè)實(shí)例呢奏瞬?

很容易想到枫绅,可以把實(shí)例放到頂層對(duì)象global。

// mod.js
function A() {
  this.foo = 'hello';
}

if (!global._foo) {
  global._foo = new A();
}

module.exports = global._foo;

然后硼端,加載上面的mod.js并淋。

const a = require('./mod.js');
console.log(a.foo);

上面代碼中,變量a任何時(shí)候加載的都是A的同一個(gè)實(shí)例珍昨。

但是县耽,這里有一個(gè)問題,全局變量global._foo是可寫的镣典,任何文件都可以修改兔毙。
為了防止這種情況出現(xiàn),我們就可以使用 Symbol兄春。

// mod.js
const FOO_KEY = Symbol.for('foo');

function A() {
  this.foo = 'hello';
}

if (!global[FOO_KEY]) {
  global[FOO_KEY] = new A();
}

module.exports = global[FOO_KEY];

但由上面的代碼可知澎剥,這個(gè)東西Symbol.for('foo');是全局的,我們?cè)谄渌胤絝or一下也可以得到這個(gè)key值赶舆,還是可以修改哑姚,所以我們可以直接用非for的Symbol方法祭饭,但是這樣也不絕對(duì)安全可靠,因?yàn)槊看螆?zhí)行腳本就會(huì)生成新的key....

內(nèi)置的 Symbol 值

除了定義自己使用的 Symbol 值以外叙量,ES6 還提供了 11 個(gè)內(nèi)置的 Symbol 值倡蝙,指向語言內(nèi)部使用的方法。

  • Symbol.hasInstance
    其他對(duì)象使用instanceof運(yùn)算符绞佩,判斷是否為該對(duì)象的實(shí)例時(shí)寺鸥,會(huì)調(diào)用這個(gè)方法。比如品山,foo instanceof Foo在語言內(nèi)部胆建,實(shí)際調(diào)用的是FooSymbol.hasInstance
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末谆奥,一起剝皮案震驚了整個(gè)濱河市眼坏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌酸些,老刑警劉巖宰译,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異魄懂,居然都是意外死亡沿侈,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門市栗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缀拭,“玉大人,你說我怎么就攤上這事填帽≈肓埽” “怎么了?”我有些...
    開封第一講書人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵篡腌,是天一觀的道長褐荷。 經(jīng)常有香客問我,道長嘹悼,這世上最難降的妖魔是什么叛甫? 我笑而不...
    開封第一講書人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮杨伙,結(jié)果婚禮上其监,老公的妹妹穿的比我還像新娘。我一直安慰自己限匣,他們只是感情好抖苦,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般睛约。 火紅的嫁衣襯著肌膚如雪鼎俘。 梳的紋絲不亂的頭發(fā)上哲身,一...
    開封第一講書人閱讀 51,231評(píng)論 1 299
  • 那天辩涝,我揣著相機(jī)與錄音,去河邊找鬼勘天。 笑死怔揩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的脯丝。 我是一名探鬼主播商膊,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼宠进!你這毒婦竟也來了晕拆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤材蹬,失蹤者是張志新(化名)和其女友劉穎实幕,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體堤器,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡昆庇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了闸溃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片整吆。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖辉川,靈堂內(nèi)的尸體忽然破棺而出表蝙,到底是詐尸還是另有隱情,我是刑警寧澤乓旗,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布府蛇,位于F島的核電站,受9級(jí)特大地震影響寸齐,放射性物質(zhì)發(fā)生泄漏欲诺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一渺鹦、第九天 我趴在偏房一處隱蔽的房頂上張望扰法。 院中可真熱鬧,春花似錦毅厚、人聲如沸塞颁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽祠锣。三九已至酷窥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間伴网,已是汗流浹背蓬推。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留澡腾,地道東北人沸伏。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像动分,于是被迫代替她去往敵國和親毅糟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354

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