感謝社區(qū)中各位的大力支持械姻,譯者再次奉上一點(diǎn)點(diǎn)福利:阿里云產(chǎn)品券,享受所有官網(wǎng)優(yōu)惠,并抽取幸運(yùn)大獎(jiǎng):點(diǎn)擊這里領(lǐng)取
數(shù)字字面量擴(kuò)展
在ES5之前葫慎,數(shù)字字面量看起來就像下面的東西 —— 八進(jìn)制形式?jīng)]有被官方指定塑径,唯一被允許的是各種瀏覽器已經(jīng)實(shí)質(zhì)上達(dá)成一致的一種擴(kuò)展:
var dec = 42,
oct = 052,
hex = 0x2a;
注意: 雖然你用不同的進(jìn)制來指定一個(gè)數(shù)字女坑,但是數(shù)字的數(shù)學(xué)值才是被存儲(chǔ)的東西,而且默認(rèn)的輸出解釋方式總是10進(jìn)制的晓勇。前面代碼段中的三個(gè)變量都在它們當(dāng)中存儲(chǔ)了值42
堂飞。
為了進(jìn)一步說明052
是一種非標(biāo)準(zhǔn)形式擴(kuò)展,考慮如下代碼:
Number( "42" ); // 42
Number( "052" ); // 52
Number( "0x2a" ); // 42
ES5繼續(xù)允許這種瀏覽器擴(kuò)展的八進(jìn)制形式(包括這樣的不一致性)绑咱,除了在strict模式下绰筛,八進(jìn)制字面量(052
)是不允許的。做出這種限制的主要原因是描融,許多開發(fā)者似乎習(xí)慣于下意識(shí)地為了將代碼對(duì)齊而在十進(jìn)制的數(shù)字前面前綴0
铝噩,然后遭遇他們完全改變了數(shù)字的值的意外!
ES6延續(xù)了除十進(jìn)制數(shù)字之外的數(shù)字字面量可以被表示的遺留的改變/種類×耍現(xiàn)在有了一種官方的八進(jìn)制形式骏庸,一種改進(jìn)了的十六進(jìn)制形式,和一種全新的二進(jìn)制形式年叮。由于Web兼容性的原因具被,在非strict模式下老式的八進(jìn)制形式052
將繼續(xù)是合法的,但其實(shí)應(yīng)當(dāng)永遠(yuǎn)不再被使用了只损。
這些是新的ES6數(shù)字字面形式:
var dec = 42,
oct = 0o52, // or `0O52` :(
hex = 0x2a, // or `0X2a` :/
bin = 0b101010; // or `0B101010` :/
唯一允許的小數(shù)形式是十進(jìn)制的一姿。八進(jìn)制,十六進(jìn)制跃惫,和二進(jìn)制都是整數(shù)形式叮叹。
而且所有這些形式的字符串表達(dá)形式都是可以被強(qiáng)制轉(zhuǎn)換/變換為它們的數(shù)字等價(jià)物的:
Number( "42" ); // 42
Number( "0o52" ); // 42
Number( "0x2a" ); // 42
Number( "0b101010" ); // 42
雖然嚴(yán)格來說不是ES6新增的,但一個(gè)鮮為人知的事實(shí)是你其實(shí)可以做反方向的轉(zhuǎn)換(好吧爆存,某種意義上的):
var a = 42;
a.toString(); // "42" —— 也可使用`a.toString( 10 )`
a.toString( 8 ); // "52"
a.toString( 16 ); // "2a"
a.toString( 2 ); // "101010"
事實(shí)上蛉顽,以這種方你可以用從2
到36
的任何進(jìn)制表達(dá)一個(gè)數(shù)字,雖然你會(huì)使用標(biāo)準(zhǔn)進(jìn)制 —— 2先较,8携冤,10,和16 ——之外的情況非常少見闲勺。
Unicode
我只能說這一節(jié)不是一個(gè)窮盡了“關(guān)于Unicode你想知道的一切”的資料曾棕。我想講解的是,你需要知道在ES6中對(duì)Unicode改變了什么霉翔,但是我們不會(huì)比這深入太多睁蕾。Mathias Bynens (http://twitter.com/mathias) 大量且出色地撰寫/講解了關(guān)于JS和Unicode (參見 https://mathiasbynens.be/notes/javascript-unicode 和 http://fluentconf.com/javascript-html-2015/public/content/2015/02/18-javascript-loves-unicode)。
從0x0000
到0xFFFF
范圍內(nèi)的Unicode字符包含了所有的標(biāo)準(zhǔn)印刷字符(以各種語言),它們都是你可能看到過和互動(dòng)過的子眶。這組字符被稱為 基本多文種平面(Basic Multilingual Plane (BMP))瀑凝。BMP甚至包含像這個(gè)酷雪人一樣的有趣字符: ? (U+2603)。
在這個(gè)BMP集合之外還有許多擴(kuò)展的Unicode字符臭杰,它們的范圍一直到0x10FFFF
粤咪。這些符號(hào)經(jīng)常被稱為 星形(astral) 符號(hào),這正是BMP之外的字符的16組 平面 (也就是渴杆,分層/分組)的名稱寥枝。星形符號(hào)的例子包括?? (U+1D11E)和?? (U+1F4A9)。
在ES6之前磁奖,JavaScript字符串可以使用Unicode轉(zhuǎn)義來指定Unicode字符囊拜,例如:
var snowman = "\u2603";
console.log( snowman ); // "?"
然而实夹,\uXXXX
Unicode轉(zhuǎn)義僅支持四個(gè)十六進(jìn)制字符绳泉,所以用這種方式表示你只能表示BMP集合中的字符。要在ES6以前使用Unicode轉(zhuǎn)義表示一個(gè)星形字符厨诸,你需要使用一個(gè) 代理對(duì)(surrogate pair) —— 基本上是兩個(gè)經(jīng)特殊計(jì)算的Unicode轉(zhuǎn)義字符放在一起身诺,被JS解釋為一個(gè)單獨(dú)星形字符:
var gclef = "\uD834\uDD1E";
console.log( gclef ); // "??"
在ES6中蜜托,我們現(xiàn)在有了一種Unicode轉(zhuǎn)義的新形式(在字符串和正則表達(dá)式中),稱為Unicode 代碼點(diǎn)轉(zhuǎn)義:
var gclef = "\u{1D11E}";
console.log( gclef ); // "??"
如你所見霉赡,它的區(qū)別是出現(xiàn)在轉(zhuǎn)義序列中的{ }
橄务,它允許轉(zhuǎn)義序列中包含任意數(shù)量的十六進(jìn)制字符。因?yàn)槟阒恍枰鶄€(gè)就可以表示在Unicode中可能的最高代碼點(diǎn)(也就是穴亏,0x10FFFF)蜂挪,所以這是足夠的。
Unicode敏感的字符串操作
在默認(rèn)情況下迫肖,JavaScript字符串操作和方法對(duì)字符串值中的星形符號(hào)是不敏感的锅劝。所以攒驰,它們獨(dú)立地處理每個(gè)BMP字符蟆湖,即便是可以組成一個(gè)單獨(dú)字符的兩半代理〔7啵考慮如下代碼:
var snowman = "?";
snowman.length; // 1
var gclef = "??";
gclef.length; // 2
那么隅津,我們?nèi)绾尾拍苷_地計(jì)算這樣的字符串的長度呢?在這種場景下劲室,下面的技巧可以工作:
var gclef = "??";
[...gclef].length; // 1
Array.from( gclef ).length; // 1
回想一下本章早先的“for..of
循環(huán)”一節(jié)伦仍,ES6字符串擁有內(nèi)建的迭代器。這個(gè)迭代器恰好是Unicode敏感的很洋,這意味著它將自動(dòng)地把一個(gè)星形符號(hào)作為一個(gè)單獨(dú)的值輸出充蓝。我們?cè)谝粋€(gè)數(shù)組字面量上使用擴(kuò)散操作符...
,利用它創(chuàng)建了一個(gè)字符串符號(hào)的數(shù)組。然后我們只需檢查這個(gè)結(jié)果數(shù)組的長度谓苟。ES6的Array.from(..)
基本上與[...XYZ]
做的事情相同官脓,不過我們將在第六章中講解這個(gè)工具的細(xì)節(jié)。
警告: 應(yīng)當(dāng)注意的是涝焙,相對(duì)地講卑笨,與理論上經(jīng)過優(yōu)化的原生工具/屬性將做的事情比起來,僅僅為了得到一個(gè)字符串的長度就構(gòu)建并耗盡一個(gè)迭代器在性能上的代價(jià)是高昂的仑撞。
不幸的是赤兴,完整的答案并不簡單或直接。除了代理對(duì)(字符串迭代器可以搞定的)隧哮,一些特殊的Unicode代碼點(diǎn)有其他特殊的行為桶良,解釋起來非常困難。例如沮翔,有一組代碼點(diǎn)可以修改前一個(gè)相鄰的字符艺普,稱為 組合變音符號(hào)(Combining Diacritical Marks)
考慮這兩個(gè)數(shù)組的輸出:
console.log( s1 ); // "é"
console.log( s2 ); // "é"
它們看起來一樣,但它們不是鉴竭!這是我們?nèi)绾蝿?chuàng)建s1
和s2
的:
var s1 = "\xE9",
s2 = "e\u0301";
你可能猜到了歧譬,我們前面的length
技巧對(duì)s2
不管用:
[...s1].length; // 1
[...s2].length; // 2
那么我們能做什么?在這種情況下搏存,我們可以使用ES6的String#normalize(..)
工具瑰步,在查詢這個(gè)值的長度前對(duì)它實(shí)施一個(gè) Unicode正規(guī)化操作:
var s1 = "\xE9",
s2 = "e\u0301";
s1.normalize().length; // 1
s2.normalize().length; // 1
s1 === s2; // false
s1 === s2.normalize(); // true
實(shí)質(zhì)上,normalize(..)
接受一個(gè)"e\u0301"
這樣的序列璧眠,并把它正規(guī)化為\xE9
缩焦。正規(guī)化甚至可以組合多個(gè)相鄰的組合符號(hào),如果存在適合他們組合的Unicode字符的話:
var s1 = "o\u0302\u0300",
s2 = s1.normalize(),
s3 = "?";
s1.length; // 3
s2.length; // 1
s3.length; // 1
s2 === s3; // true
不幸的是责静,這里的正規(guī)化也不完美袁滥。如果你有多個(gè)組合符號(hào)在修改一個(gè)字符,你可能不會(huì)得到你所期望的長度計(jì)數(shù)灾螃,因?yàn)橐粋€(gè)被獨(dú)立定義的题翻,可以表示所有這些符號(hào)組合的正規(guī)化字符可能不存在。例如:
var s1 = "e\u0301\u0330";
console.log( s1 ); // "e??"
s1.normalize().length; // 2
你越深入這個(gè)兔子洞腰鬼,你就越能理解要得到一個(gè)“長度”的精確定義是很困難的嵌赠。我們?cè)谝曈X上看到的作為一個(gè)單獨(dú)字符繪制的東西 —— 更精確地說,它稱為一個(gè) 字形 —— 在程序處理的意義上不總是嚴(yán)格地關(guān)聯(lián)到一個(gè)單獨(dú)的“字符”上熄赡。
提示: 如果你就是想看看這個(gè)兔子洞有多深姜挺,看看“字形群集邊界(Grapheme Cluster Boundaries)”算法(http://www.Unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries)。
字符定位
與長度的復(fù)雜性相似彼硫,“在位置2上的字符是什么炊豪?”凌箕,這么問的意思究竟是什么?前ES6的原生答案來自charAt(..)
词渤,它不會(huì)遵守一個(gè)星形字符的原子性陌知,也不會(huì)考慮組合符號(hào)。
考慮如下代碼:
var s1 = "abc\u0301d",
s2 = "ab\u0107d",
s3 = "ab\u{1d49e}d";
console.log( s1 ); // "ab?d"
console.log( s2 ); // "ab?d"
console.log( s3 ); // "ab??d"
s1.charAt( 2 ); // "c"
s2.charAt( 2 ); // "?"
s3.charAt( 2 ); // "" <-- 不可打印的代理字符
s3.charAt( 3 ); // "" <-- 不可打印的代理字符
那么掖肋,ES6會(huì)給我們Unicode敏感版本的charAt(..)
嗎仆葡?不幸的是,不志笼。在本書寫作時(shí)沿盅,在后ES6的考慮之中有一個(gè)這樣的工具的提案。
但是使用我們?cè)谇耙还?jié)探索的東西(當(dāng)然也帶著它的限制H依!)腰涧,我們可以黑一個(gè)ES6的答案:
var s1 = "abc\u0301d",
s2 = "ab\u0107d",
s3 = "ab\u{1d49e}d";
[...s1.normalize()][2]; // "?"
[...s2.normalize()][2]; // "?"
[...s3.normalize()][2]; // "??"
警告: 提醒一個(gè)早先的警告:在每次你想得到一個(gè)單獨(dú)的字符時(shí)構(gòu)建并耗盡一個(gè)迭代器……在性能上不是很理想。對(duì)此紊浩,希望我們很快能在后ES6時(shí)代得到一個(gè)內(nèi)建的窖铡,優(yōu)化過的工具。
那么charCodeAt(..)
工具的Unicode敏感版本呢坊谁?ES6給了我們codePointAt(..)
:
var s1 = "abc\u0301d",
s2 = "ab\u0107d",
s3 = "ab\u{1d49e}d";
s1.normalize().codePointAt( 2 ).toString( 16 );
// "107"
s2.normalize().codePointAt( 2 ).toString( 16 );
// "107"
s3.normalize().codePointAt( 2 ).toString( 16 );
// "1d49e"
那么從另一個(gè)方向呢费彼?String.fromCharCode(..)
的Unicode敏感版本是ES6的String.fromCodePoint(..)
:
String.fromCodePoint( 0x107 ); // "?"
String.fromCodePoint( 0x1d49e ); // "??"
那么等一下,我們能組合String.fromCodePoint(..)
與codePointAt(..)
來得到一個(gè)剛才的Unicode敏感charAt(..)
的更好版本嗎口芍?是的箍铲!
var s1 = "abc\u0301d",
s2 = "ab\u0107d",
s3 = "ab\u{1d49e}d";
String.fromCodePoint( s1.normalize().codePointAt( 2 ) );
// "?"
String.fromCodePoint( s2.normalize().codePointAt( 2 ) );
// "?"
String.fromCodePoint( s3.normalize().codePointAt( 2 ) );
// "??"
還有好幾個(gè)字符串方法我們沒有在這里講解,包括toUpperCase()
鬓椭,toLowerCase()
颠猴,substring(..)
,indexOf(..)
小染,slice(..)
翘瓮,以及其他十幾個(gè)。它們中沒有任何一個(gè)為了完全支持Unicode而被改變或增強(qiáng)過裤翩,所以在處理含有星形符號(hào)的字符串是资盅,你應(yīng)當(dāng)非常小心 —— 可能干脆回避它們!
還有幾個(gè)字符串方法為了它們的行為而使用正則表達(dá)式岛都,比如replace(..)
和match(..)
律姨。值得慶幸的是振峻,ES6為正則表達(dá)式帶來了Unicode支持臼疫,正如我們?cè)诒菊略缜暗摹癠nicode標(biāo)志”中講解過的那樣。
好了扣孟,就是這些烫堤!有了我們剛剛講過的各種附加功能,JavaScript的Unicode字符串支持要比前ES6時(shí)代好太多了(雖然還不完美)。
Unicode標(biāo)識(shí)符名稱
Unicode還可以被用于標(biāo)識(shí)符名稱(變量鸽斟,屬性拔创,等等)。在ES6之前富蓄,你可以通過Unicode轉(zhuǎn)義這么做剩燥,比如:
var \u03A9 = 42;
// 等同于:var Ω = 42;
在ES6中,你還可以使用前面講過的代碼點(diǎn)轉(zhuǎn)義語法:
var \u{2B400} = 42;
// 等同于:var ?? = 42;
關(guān)于究竟哪些Unicode字符被允許使用立倍,有一組復(fù)雜的規(guī)則灭红。另外,有些字符只要不是標(biāo)識(shí)符名稱的第一個(gè)字符就允許使用口注。
注意: 關(guān)于所有這些細(xì)節(jié)变擒,Mathias Bynens寫了一篇了不起的文章 (https://mathiasbynens.be/notes/javascript-identifiers-es6)。
很少有理由寝志,或者是為了學(xué)術(shù)上的目的娇斑,才會(huì)在標(biāo)識(shí)符名稱中使用這樣不尋常的字符。你通常不會(huì)因?yàn)橐揽窟@些深?yuàn)W的功能編寫代碼而感到舒服材部。
Symbol
在ES6中毫缆,長久以來首次,有一個(gè)新的基本類型被加入到了JavaScript:symbol
乐导。但是悔醋,與其他的基本類型不同,symbol沒有字面形式兽叮。
這是你如何創(chuàng)建一個(gè)symbol:
var sym = Symbol( "some optional description" );
typeof sym; // "symbol"
一些要注意的事情是:
- 你不能也不應(yīng)該將
new
與Symbol(..)
一起使用芬骄。它不是一個(gè)構(gòu)造器,你也不是在產(chǎn)生一個(gè)對(duì)象鹦聪。 - 被傳入
Symbol(..)
的參數(shù)是可選的账阻。如果傳入的話,它應(yīng)當(dāng)是一個(gè)字符串泽本,為symbol的目的給出一個(gè)友好的描述淘太。 -
typeof
的輸出是一個(gè)新的值("symbol"
),這是識(shí)別一個(gè)symbol的主要方法规丽。
如果描述被提供的話蒲牧,它僅僅用于symbol的字符串化表示:
sym.toString(); // "Symbol(some optional description)"
與基本字符串值如何不是String
的實(shí)例的原理很相似,symbol也不是Symbol
的實(shí)例赌莺。如果冰抢,由于某些原因,你想要為一個(gè)symbol值構(gòu)建一個(gè)封箱的包裝器對(duì)像艘狭,你可以做如下的事情:
sym instanceof Symbol; // false
var symObj = Object( sym );
symObj instanceof Symbol; // true
symObj.valueOf() === sym; // true
注意: 在這個(gè)代碼段中的symObj
和sym
是可以互換使用的挎扰;兩種形式可以在symbol被用到的地方使用翠订。沒有太多的理由要使用封箱的包裝對(duì)象形式(symObj
),而不用基本類型形式(sym
)遵倦。和其他基本類型的建議相似尽超,使用sym
而非symObj
可能是最好的。
一個(gè)symbol本身的內(nèi)部值 —— 稱為它的name
—— 被隱藏在代碼之外而不能取得梧躺。你可以認(rèn)為這個(gè)symbol的值是一個(gè)自動(dòng)生成的似谁,(在你的應(yīng)用程序中)獨(dú)一無二的字符串值。
但如果這個(gè)值是隱藏且不可取得的掠哥,那么擁有一個(gè)symbol還有什么意義棘脐?
一個(gè)symbol的主要意義是創(chuàng)建一個(gè)不會(huì)和其他任何值沖突的類字符串值。所以龙致,舉例來說蛀缝,可以考慮將一個(gè)symbol用做表示一個(gè)事件的名稱的值:
const EVT_LOGIN = Symbol( "event.login" );
然后你可以在一個(gè)使用像"event.login"
這樣的一般字符串字面量的地方使用EVT_LOGIN
:
evthub.listen( EVT_LOGIN, function(data){
// ..
} );
其中的好處是,EVT_LOGIN
持有一個(gè)不能被其他任何值所(有意或無意地)重復(fù)的值目代,所以在哪個(gè)事件被分發(fā)或處理的問題上不可能存在任何含糊屈梁。
注意: 在前面的代碼段的幕后,幾乎可以肯定地認(rèn)為evthub
工具使用了EVT_LOGIN
參數(shù)值的symbol值作為某個(gè)跟蹤事件處理器的內(nèi)部對(duì)象的屬性/鍵榛了。如果evthub
需要將symbol值作為一個(gè)真實(shí)的字符串使用在讶,那么它將需要使用String(..)
或者toString(..)
進(jìn)行明確強(qiáng)制轉(zhuǎn)換,因?yàn)閟ymbol的隱含字符串強(qiáng)制轉(zhuǎn)換是不允許的霜大。
你可能會(huì)將一個(gè)symbol直接用做一個(gè)對(duì)象中的屬性名/鍵构哺,如此作為一個(gè)你想將之用于隱藏或元屬性的特殊屬性。重要的是战坤,要知道雖然你試圖這樣對(duì)待它曙强,但是它 實(shí)際上 并不是隱藏或不可接觸的屬性。
考慮這個(gè)實(shí)現(xiàn)了 單例 模式行為的模塊 —— 也就是途茫,它僅允許自己被創(chuàng)建一次:
const INSTANCE = Symbol( "instance" );
function HappyFace() {
if (HappyFace[INSTANCE]) return HappyFace[INSTANCE];
function smile() { .. }
return HappyFace[INSTANCE] = {
smile: smile
};
}
var me = HappyFace(),
you = HappyFace();
me === you; // true
這里的symbol值INSTANCE
是一個(gè)被靜態(tài)地存儲(chǔ)在HappyFace()
函數(shù)對(duì)象上的特殊的碟嘴,幾乎是隱藏的,類元屬性囊卜。
替代性地娜扇,它本可以是一個(gè)像__instance
這樣的普通屬性,而且其行為將會(huì)是一模一樣的栅组。symbol的使用僅僅增強(qiáng)了程序元編程的風(fēng)格雀瓢,將這個(gè)INSTANCE
屬性與其他普通的屬性間保持隔離。
Symbol注冊(cè)表
在前面幾個(gè)例子中使用symbol的一個(gè)微小的缺點(diǎn)是玉掸,變量EVT_LOGIN
和INSTANCE
不得不存儲(chǔ)在外部作用域中(甚至也許是全局作用域)刃麸,或者用某種方法存儲(chǔ)在一個(gè)可用的公共位置,這樣代碼所有需要使用這些symbol的部分都可以訪問它們排截。
為了輔助組織訪問這些symbol的代碼嫌蚤,你可以使用 全局symbol注冊(cè)表 來創(chuàng)建symbol辐益。例如:
const EVT_LOGIN = Symbol.for( "event.login" );
console.log( EVT_LOGIN ); // Symbol(event.login)
和:
function HappyFace() {
const INSTANCE = Symbol.for( "instance" );
if (HappyFace[INSTANCE]) return HappyFace[INSTANCE];
// ..
return HappyFace[INSTANCE] = { .. };
}
Symbol.for(..)
查詢?nèi)謘ymbol注冊(cè)表來查看一個(gè)symbol是否已經(jīng)使用被提供的說明文本存儲(chǔ)過了断傲,如果有就返回它脱吱。如果沒有,就創(chuàng)建一個(gè)并返回认罩。換句話說箱蝠,全局symbol注冊(cè)表通過描述文本將symbol值看作它們本身的單例。
但這也意味著只要使用匹配的描述名垦垂,你的應(yīng)用程序的任何部分都可以使用Symbol.for(..)
從注冊(cè)表中取得symbol宦搬。
諷刺的是,基本上symbol的本意是在你的應(yīng)用程序中取代 魔法字符串 的使用(被賦予了特殊意義的隨意的字符串值)劫拗。但是你正是在全局symbol注冊(cè)表中使用 魔法 描述字符串值來唯一識(shí)別/定位它們的间校!
為了避免意外的沖突,你可能想使你的symbol描述十分獨(dú)特页慷。這么做的一個(gè)簡單的方法是在它們之中包含前綴/環(huán)境/名稱空間的信息憔足。
例如,考慮一個(gè)像下面這樣的工具:
function extractValues(str) {
var key = Symbol.for( "extractValues.parse" ),
re = extractValues[key] ||
/[^=&]+?=([^&]+?)(?=&|$)/g,
values = [], match;
while (match = re.exec( str )) {
values.push( match[1] );
}
return values;
}
我們使用魔法字符串值"extractValues.parse"
酒繁,因?yàn)樵谧?cè)表中的其他任何symbol都不太可能與這個(gè)描述相沖突滓彰。
如果這個(gè)工具的一個(gè)用戶想要覆蓋這個(gè)解析用的正則表達(dá)式,他們也可以使用symbol注冊(cè)表:
extractValues[Symbol.for( "extractValues.parse" )] =
/..some pattern../g;
extractValues( "..some string.." );
除了symbol注冊(cè)表在全局地存儲(chǔ)這些值上提供的協(xié)助以外州袒,我們?cè)谶@里看到的一切其實(shí)都可以通過將魔法字符串"extractValues.parse"
作為一個(gè)鍵揭绑,而不是一個(gè)symbol,來做到郎哭。這其中在元編程的層次上的改進(jìn)要多于在函數(shù)層次上的改進(jìn)他匪。
你可能偶然會(huì)使用一個(gè)已經(jīng)被存儲(chǔ)在注冊(cè)表中的symbol值來查詢它底層存儲(chǔ)了什么描述文本(鍵)。例如夸研,因?yàn)槟銦o法傳遞symbol值本身诚纸,你可能需要通知你的應(yīng)用程序的另一個(gè)部分如何在注冊(cè)表中定位一個(gè)symbol。
你可以使用Symbol.keyFor(..)
取得一個(gè)被注冊(cè)的symbol描述文本(鍵):
var s = Symbol.for( "something cool" );
var desc = Symbol.keyFor( s );
console.log( desc ); // "something cool"
// 再次從注冊(cè)表取得symbol
var s2 = Symbol.for( desc );
s2 === s; // true
Symbols作為對(duì)象屬性
如果一個(gè)symbol被用作一個(gè)對(duì)象的屬性/鍵陈惰,它會(huì)被以一種特殊的方式存儲(chǔ)畦徘,以至這個(gè)屬性不會(huì)出現(xiàn)在這個(gè)對(duì)象屬性的普通枚舉中:
var o = {
foo: 42,
[ Symbol( "bar" ) ]: "hello world",
baz: true
};
Object.getOwnPropertyNames( o ); // [ "foo","baz" ]
要取得對(duì)象的symbol屬性:
Object.getOwnPropertySymbols( o ); // [ Symbol(bar) ]
這表明一個(gè)屬性symbol實(shí)際上不是隱藏的或不可訪問的,因?yàn)槟憧偸强梢栽?code>Object.getOwnPropertySymbols(..)的列表中看到它抬闯。
內(nèi)建Symbols
ES6帶來了好幾種預(yù)定義的內(nèi)建symbol井辆,它們暴露了在JavaScript對(duì)象值上的各種元行為。然而溶握,正如人們所預(yù)料的那樣杯缺,這些symbol 沒有 沒被注冊(cè)到全局symbol注冊(cè)表中。
取而代之的是睡榆,它們作為屬性被存儲(chǔ)到了Symbol
函數(shù)對(duì)象中萍肆。例如袍榆,在本章早先的“for..of
”一節(jié)中,我們介紹了值Symbol.iterator
:
var a = [1,2,3];
a[Symbol.iterator]; // native function
語言規(guī)范使用@@
前綴注釋指代內(nèi)建的symbol塘揣,最常見的幾個(gè)是:@@iterator
包雀,@@toStringTag
,@@toPrimitive
亲铡。還定義了幾個(gè)其他的symbol才写,雖然他們可能不那么頻繁地被使用。
注意: 關(guān)于這些內(nèi)建symbol如何被用于元編程的詳細(xì)信息奖蔓,參見第七章的“通用Symbol”赞草。
復(fù)習(xí)
ES6給JavaScript增加了一堆新的語法形式,有好多東西要學(xué)吆鹤!
這些東西中的大多數(shù)都是為了緩解常見編程慣用法中的痛點(diǎn)而設(shè)計(jì)的厨疙,比如為函數(shù)參數(shù)設(shè)置默認(rèn)值和將“剩余”的參數(shù)收集到一個(gè)數(shù)組中。解構(gòu)是一個(gè)強(qiáng)大的工具疑务,用來更簡約地表達(dá)從數(shù)組或嵌套對(duì)象的賦值沾凄。
雖然像箭頭函數(shù)=>
這樣的特性看起來也都是關(guān)于更簡短更好看的語法,但是它們實(shí)際上擁有非常特殊的行為暑始,你應(yīng)當(dāng)在恰當(dāng)?shù)那闆r下有意地使用它們搭独。
擴(kuò)展的Unicode支持,新的正則表達(dá)式技巧廊镜,和新的symbol
基本類型充實(shí)了ES6語法的發(fā)展演變牙肝。