解析JavaScript中的字符串類型與字符編碼支持

JavaScript中的字符串也像Python那樣支持反斜杠的轉(zhuǎn)移,并且字符集方面默認(rèn)為Unicode,下面就來(lái)詳細(xì)解析JavaScript中的字符串類型與字符編碼支持

[圖片上傳失敗...(image-c7443f-1542182027138)]

定義 字符串就是零個(gè)或多個(gè)排在一起的字符,放在單引號(hào)或雙引號(hào)之中息拜。

`'abc'`
`"abc"`

單引號(hào)字符串的內(nèi)部邢锯,可以使用雙引號(hào)。雙引號(hào)字符串的內(nèi)部医瘫,可以使用單引號(hào)况鸣。

'key = "value"'
"It's a long journey"

上面兩個(gè)都是合法的字符串。

如果要在單引號(hào)字符串的內(nèi)部幼衰,使用單引號(hào)(或者在雙引號(hào)字符串的內(nèi)部阿弃,使用雙引號(hào)),就必須在內(nèi)部的單引號(hào)(或者雙引號(hào))前面加上反斜杠羞延,用來(lái)轉(zhuǎn)義渣淳。

`'Did she say \'Hello\'?'`
`// "Did she say 'Hello'?"`
`"Did she say \"Hello\"?"`
`// "Did she say "Hello"?"`

由于HTML語(yǔ)言的屬性值使用雙引號(hào),所以很多項(xiàng)目約定JavaScript語(yǔ)言的字符串只使用單引號(hào)伴箩,本教程就遵守這個(gè)約定入愧。當(dāng)然,只使用雙引號(hào)也完全可以嗤谚。重要的是棺蛛,堅(jiān)持使用一種風(fēng)格,不要兩種風(fēng)格混合巩步。

字符串默認(rèn)只能寫在一行內(nèi)旁赊,分成多行將會(huì)報(bào)錯(cuò)。

`'a`
`b`
`c'`
`// SyntaxError: Unexpected token ILLEGAL`

上面代碼將一個(gè)字符串分成三行椅野,JavaScript就會(huì)報(bào)錯(cuò)终畅。
如果長(zhǎng)字符串必須分成多行籍胯,可以在每一行的尾部使用反斜杠。

`var` `longString =` `"Long \`
`long \`
`long \`
`string"``;`
`logString`
`// "Long long long string"`

上面代碼表示离福,加了反斜杠以后杖狼,原來(lái)寫在一行的字符串,可以分成多行書(shū)寫妖爷。但是蝶涩,輸出的時(shí)候還是單行,效果與寫在同一行完全一樣絮识。注意绿聘,反斜杠的后面必須是換行符,而不能有其他字符(比如空格)笋除,否則會(huì)報(bào)錯(cuò)斜友。

連接運(yùn)算符(+)可以連接多個(gè)單行字符串,將長(zhǎng)字符串拆成多行書(shū)寫垃它,輸出的時(shí)候也是單行鲜屏。

`var` `longString =` `'Long '`
`+` `'long '`
`+` `'long '`
`+` `'string'``;`

如果想輸出多行字符串,有一種利用多行注釋的變通方法国拇。

`(``function` `() {` `/*`
`line 1`
`line 2`
`line 3`
`*/``}).toString().split(``'\n'``).slice(1, -1).join(``'\n'``)`
`// "line 1`
`// line 2`
`// line 3"`

上面的例子中洛史,輸出的字符串就是多行。

轉(zhuǎn)義 反斜杠(\)在字符串內(nèi)有特殊含義酱吝,用來(lái)表示一些特殊字符也殖,所以又稱為轉(zhuǎn)義符。

需要用反斜杠轉(zhuǎn)義的特殊字符务热,主要有下面這些:

*   \0 null(\u0000)
*   \b 后退鍵(\u0008)
*   \f 換頁(yè)符(\u000C)
*   \n 換行符(\u000A)
*   \r 回車鍵(\u000D)
*   \t 制表符(\u0009)
*   \v 垂直制表符(\u000B)
*   \' 單引號(hào)(\u0027)
*   \" 雙引號(hào)(\u0022)
*   \ 反斜杠(\u005C)

上面這些字符前面加上反斜杠忆嗜,都表示特殊含義。

`console.log(``'1\n2'``)`
`// 1`
`// 2`

上面代碼中崎岂,\n表示換行捆毫,輸出的時(shí)候就分成了兩行。
反斜杠還有三種特殊用法冲甘。

  • (1)\HHH
    反斜杠后面緊跟三個(gè)八進(jìn)制數(shù)(000到377)绩卤,代表一個(gè)字符。HHH對(duì)應(yīng)該字符的Unicode碼點(diǎn)江醇,比如\251表示版權(quán)符號(hào)濒憋。顯然,這種方法只能輸出256種字符陶夜。

  • (2)\xHH
    \x后面緊跟兩個(gè)十六進(jìn)制數(shù)(00到FF)凛驮,代表一個(gè)字符。HH對(duì)應(yīng)該字符的Unicode碼點(diǎn)律适,比如\xA9表示版權(quán)符號(hào)辐烂。這種方法也只能輸出256種字符遏插。

  • (3)\uXXXX
    \u后面緊跟四個(gè)十六進(jìn)制數(shù)(0000到FFFF),代表一個(gè)字符纠修。HHHH對(duì)應(yīng)該字符的Unicode碼點(diǎn)胳嘲,比如\u00A9表示版權(quán)符號(hào)。

下面是這三種字符特殊寫法的例子扣草。

`'\251'` `// "?"`
`'\xA9'` `// "?"`
`'\u00A9'` `// "?"`
`'172'` `===` `'z'` `// true`
`'\x7A'` `===` `'z'` `// true`
`'\u007A'` `===` `'z'` `// true`

如果在非特殊字符前面使用反斜杠了牛,則反斜杠會(huì)被省略。

`'\a'`
`// "a"`

上面代碼中辰妙,a是一個(gè)正常字符鹰祸,前面加反斜杠沒(méi)有特殊含義,反斜杠會(huì)被自動(dòng)省略密浑。
如果字符串的正常內(nèi)容之中蛙婴,需要包含反斜杠,則反斜杠前面需要再加一個(gè)反斜杠尔破,用來(lái)對(duì)自身轉(zhuǎn)義街图。

`"Prev \\ Next"`
`// "Prev \ Next"`

字符串與數(shù)組 字符串可以被視為字符數(shù)組,因此可以使用數(shù)組的方括號(hào)運(yùn)算符懒构,用來(lái)返回某個(gè)位置的字符(位置編號(hào)從0開(kāi)始)餐济。

`var` `s =` `'hello'``;`
`s[0]` `// "h"`
`s[1]` `// "e"`
`s[4]` `// "o"`

// 直接對(duì)字符串使用方括號(hào)運(yùn)算符
'hello'``[1] // "e"

如果方括號(hào)中的數(shù)字超過(guò)字符串的長(zhǎng)度,或者方括號(hào)中根本不是數(shù)字胆剧,則返回undefined絮姆。

`'abc'``[3]` `// undefined`
`'abc'``[-1]` `// undefined`
`'abc'``[``'x'``]` `// undefined`

但是,字符串與數(shù)組的相似性僅此而已秩霍。實(shí)際上篙悯,無(wú)法改變字符串之中的單個(gè)字符。

`var` `s =` `'hello'``;`
`delete` `s[0];`
`s` `// "hello"`
`s[1] =` `'a'``;`
`s` `// "hello"`
`s[5] =` `'!'``;`
`s  `// "hello"`

上面代碼表示铃绒,字符串內(nèi)部的單個(gè)字符無(wú)法改變和增刪辕近,這些操作會(huì)默默地失敗。

字符串之所以類似于字符數(shù)組匿垄,實(shí)際是由于對(duì)字符串進(jìn)行方括號(hào)運(yùn)算時(shí),字符串會(huì)自動(dòng)轉(zhuǎn)換為一個(gè)字符串對(duì)象归粉。

length屬性 length屬性返回字符串的長(zhǎng)度椿疗,該屬性也是無(wú)法改變的。

`var` `s =` `'hello'``;`
`s.length` `// 5`
`s.length = 3;`
`s.length` `// 5`
`s.length = 7;`
`s.length` `// 5`

上面代碼表示字符串的length屬性無(wú)法改變糠悼,但是不會(huì)報(bào)錯(cuò)届榄。

字符集 JavaScript使用Unicode字符集,也就是說(shuō)在JavaScript內(nèi)部倔喂,所有字符都用Unicode表示铝条。

不僅JavaScript內(nèi)部使用Unicode儲(chǔ)存字符靖苇,而且還可以直接在程序中使用Unicode,所有字符都可以寫成”\uxxxx”的形式班缰,其中xxxx代表該字符的Unicode編碼贤壁。比如,\u00A9代表版權(quán)符號(hào)埠忘。

`var` `s =` `'\u00A9'``;`
`s` `// "?"`

每個(gè)字符在JavaScript內(nèi)部都是以16位(即2個(gè)字節(jié))的UTF-16格式儲(chǔ)存脾拆。也就是說(shuō),JavaScript的單位字符長(zhǎng)度固定為16位長(zhǎng)度莹妒,即2個(gè)字節(jié)名船。

但是,UTF-16有兩種長(zhǎng)度:對(duì)于U+0000到U+FFFF之間的字符旨怠,長(zhǎng)度為16位(即2個(gè)字節(jié))渠驼;對(duì)于U+10000到U+10FFFF之間的字符,長(zhǎng)度為32位(即4個(gè)字節(jié))鉴腻,而且前兩個(gè)字節(jié)在0xD800到0xDBFF之間迷扇,后兩個(gè)字節(jié)在0xDC00到0xDFFF之間。舉例來(lái)說(shuō)拘哨,U+1D306對(duì)應(yīng)的字符為??谋梭,它寫成UTF-16就是0xD834 0xDF06。瀏覽器會(huì)正確將這四個(gè)字節(jié)識(shí)別為一個(gè)字符倦青,但是JavaScript內(nèi)部的字符長(zhǎng)度總是固定為16位瓮床,會(huì)把這四個(gè)字節(jié)視為兩個(gè)字符。

`var` `s =` `'\uD834\uDF06'``;`
`s` `// "??"`
`s.length` `// 2`
`/^.$/.test(s)` `// false`
`s.charAt(0)` `// ""`
`s.charAt(1)` `// ""`
`s.charCodeAt(0)` `// 55348`
`s.charCodeAt(1)` `// 57094`

上面代碼說(shuō)明产镐,對(duì)于于U+10000到U+10FFFF之間的字符隘庄,JavaScript總是視為兩個(gè)字符(字符的length屬性為2),用來(lái)匹配單個(gè)字符的正則表達(dá)式會(huì)失斞⒀恰(JavaScript認(rèn)為這里不止一個(gè)字符)丑掺,charAt方法無(wú)法返回單個(gè)字符,charCodeAt方法返回每個(gè)字節(jié)對(duì)應(yīng)的十進(jìn)制值述雾。

所以處理的時(shí)候街州,必須把這一點(diǎn)考慮在內(nèi)。對(duì)于4個(gè)字節(jié)的Unicode字符玻孟,假定C是字符的Unicode編號(hào)唆缴,H是前兩個(gè)字節(jié),L是后兩個(gè)字節(jié)黍翎,則它們之間的換算關(guān)系如下面徽。

`// 將大于U+FFFF的字符,從Unicode轉(zhuǎn)為UTF-16`
`H = Math.floor((C - 0x10000) / 0x400) + 0xD800`
`L = (C - 0x10000) % 0x400 + 0xDC00`
`// 將大于U+FFFF的字符,從UTF-16轉(zhuǎn)為Unicode`
`C = (H - 0xD800) * 0x400 + L - 0xDC00 + 0x10000`

下面的正則表達(dá)式可以識(shí)別所有UTF-16字符趟紊。

`([\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF])`

由于JavaScript引擎(嚴(yán)格說(shuō)是ES5規(guī)格)不能自動(dòng)識(shí)別輔助平面(編號(hào)大于0xFFFF)的Unicode字符氮双,導(dǎo)致所有字符串處理函數(shù)遇到這類字符,都會(huì)產(chǎn)生錯(cuò)誤的結(jié)果霎匈。如果要完成字符串相關(guān)操作戴差,就必須判斷字符是否落在0xD800到0xDFFF這個(gè)區(qū)間。

下面是能夠正確處理字符串遍歷的函數(shù)唧躲。

`function` `getSymbols(string) {`
`var` `length = string.length;`
`var` `index = -1;`
`var` `output = [];`
`var` `character;`
`var` `charCode;`
`while` `(++index < length) {`
`character = string.charAt(index);`
`charCode = character.charCodeAt(0);`
`if` `(charCode >= 0xD800 && charCode <= 0xDBFF) {`
`output.push(character + string.charAt(++index));`
`}` `else` `{`
`output.push(character);`
`}`
`}`
`return` `output;`
`}`
`var` `symbols = getSymbols(``'??'``);`
`symbols.forEach(``function``(symbol) {`
`// 
`});`

替換(String.prototype.replace)造挽、截取子字符串(String.prototype.substring, String.prototype.slice)等其他字符串操作,都必須做類似的處理弄痹。

Base64轉(zhuǎn)碼 Base64是一種編碼方法饭入,可以將任意字符轉(zhuǎn)成可打印字符。使用這種編碼方法肛真,主要不是為了加密谐丢,而是為了不出現(xiàn)特殊字符,簡(jiǎn)化程序的處理蚓让。

JavaScript原生提供兩個(gè)Base64相關(guān)方法乾忱。

*   btoa():字符串或二進(jìn)制值轉(zhuǎn)為Base64編碼
*   atob():Base64編碼轉(zhuǎn)為原來(lái)的編碼
`var` `string =` `'Hello World!'``;`
`btoa(string)` `// "SGVsbG8gV29ybGQh"`
`atob(``'SGVsbG8gV29ybGQh'``)` `// "Hello World!"`

這兩個(gè)方法不適合非ASCII碼的字符,會(huì)報(bào)錯(cuò)历极。

`btoa(``'你好'``)`
`// Uncaught DOMException: The string to be encoded contains characters outside of the Latin1 range.`

要將非ASCII碼字符轉(zhuǎn)為Base64編碼窄瘟,必須中間插入一個(gè)轉(zhuǎn)碼環(huán)節(jié),再使用這兩個(gè)方法趟卸。

`function` `b64Encode(str) {`
`return` `btoa(encodeURIComponent(str));`
`}`
`function` `b64Decode(str) {`
`return` `decodeURIComponent(atob(str));`
`}`
`b64Encode(``'你好'``)` `// "JUU0JUJEJUEwJUU1JUE1JUJE"`
`b64Decode(``'JUU0JUJEJUEwJUU1JUE1JUJE'``)` `// "你好"`
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蹄葱,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子锄列,更是在濱河造成了極大的恐慌图云,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件邻邮,死亡現(xiàn)場(chǎng)離奇詭異竣况,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)筒严,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門丹泉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人鸭蛙,你說(shuō)我怎么就攤上這事嘀掸。” “怎么了规惰?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)泉蝌。 經(jīng)常有香客問(wèn)我歇万,道長(zhǎng)揩晴,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任贪磺,我火速辦了婚禮硫兰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘寒锚。我一直安慰自己劫映,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布刹前。 她就那樣靜靜地躺著泳赋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪喇喉。 梳的紋絲不亂的頭發(fā)上祖今,一...
    開(kāi)封第一講書(shū)人閱讀 52,394評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音拣技,去河邊找鬼千诬。 笑死,一個(gè)胖子當(dāng)著我的面吹牛膏斤,可吹牛的內(nèi)容都是我干的徐绑。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼莫辨,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼傲茄!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起衔掸,我...
    開(kāi)封第一講書(shū)人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤烫幕,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后敞映,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體较曼,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年振愿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了捷犹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡冕末,死狀恐怖萍歉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情档桃,我是刑警寧澤枪孩,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響蔑舞,放射性物質(zhì)發(fā)生泄漏拒担。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一攻询、第九天 我趴在偏房一處隱蔽的房頂上張望从撼。 院中可真熱鬧,春花似錦钧栖、人聲如沸低零。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)掏婶。三九已至,卻和暖如春阴挣,著一層夾襖步出監(jiān)牢的瞬間气堕,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工畔咧, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留茎芭,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓誓沸,卻偏偏與公主長(zhǎng)得像梅桩,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子拜隧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

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