解析JavaScript中的字符串類型與字符編碼支持
定義
字符串就是零個(gè)或多個(gè)排在一起的字符,放在單引號(hào)或雙引號(hào)之中。
'abc' "abc"
'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))前面加上反斜杠,用來轉(zhuǎn)義。
'Did she say \'Hello\'?' // "Did she say 'Hello'?" "Did she say \"Hello\"?" // "Did she say "Hello"?"
由于HTML語言的屬性值使用雙引號(hào),所以很多項(xiàng)目約定JavaScript語言的字符串只使用單引號(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"; longString // "Long long long string"
上面代碼表示,加了反斜杠以后,原來寫在一行的字符串,可以分成多行書寫。但是,輸出的時(shí)候還是單行,效果與寫在同一行完全一樣。注意,反斜杠的后面必須是換行符,而不能有其他字符(比如空格),否則會(huì)報(bào)錯(cuò)。
連接運(yùn)算符(+)可以連接多個(gè)單行字符串,將長(zhǎng)字符串拆成多行書寫,輸出的時(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)有特殊含義,用來表示一些特殊字符,所以又稱為轉(zhuǎn)義符。
需要用反斜杠轉(zhuǎn)義的特殊字符,主要有下面這些:
- \0 null(\u0000)
- \b 后退鍵(\u0008)
- \f 換頁符(\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è)正常字符,前面加反斜杠沒有特殊含義,反斜杠會(huì)被自動(dòng)省略。
如果字符串的正常內(nèi)容之中,需要包含反斜杠,則反斜杠前面需要再加一個(gè)反斜杠,用來對(duì)自身轉(zhuǎn)義。
"Prev \\ Next" // "Prev \ Next"
字符串與數(shù)組
字符串可以被視為字符數(shù)組,因此可以使用數(shù)組的方括號(hào)運(yùn)算符,用來返回某個(gè)位置的字符(位置編號(hào)從0開始)。
var s = 'hello'; s[0] // "h" s[1] // "e" s[4] // "o" // 直接對(duì)字符串使用方括號(hào)運(yùn)算符 'hello'[1] // "e"
如果方括號(hào)中的數(shù)字超過字符串的長(zhǎng)度,或者方括號(hào)中根本不是數(shù)字,則返回undefined。
'abc'[3] // undefined 'abc'[-1] // undefined 'abc'['x'] // undefined
但是,字符串與數(shù)組的相似性僅此而已。實(shí)際上,無法改變字符串之中的單個(gè)字符。
var s = 'hello'; delete s[0]; s // "hello" s[1] = 'a'; s // "hello" s[5] = '!'; s // "hello"
上面代碼表示,字符串內(nèi)部的單個(gè)字符無法改變和增刪,這些操作會(huì)默默地失敗。
字符串之所以類似于字符數(shù)組,實(shí)際是由于對(duì)字符串進(jìn)行方括號(hào)運(yùn)算時(shí),字符串會(huì)自動(dòng)轉(zhuǎn)換為一個(gè)字符串對(duì)象。
length屬性
length屬性返回字符串的長(zhǎng)度,該屬性也是無法改變的。
var s = 'hello'; s.length // 5 s.length = 3; s.length // 5 s.length = 7; s.length // 5
上面代碼表示字符串的length屬性無法改變,但是不會(huì)報(bào)錯(cuò)。
字符集
JavaScript使用Unicode字符集,也就是說在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ǔ)存。也就是說,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之間。舉例來說,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
上面代碼說明,對(duì)于于U+10000到U+10FFFF之間的字符,JavaScript總是視為兩個(gè)字符(字符的length屬性為2),用來匹配單個(gè)字符的正則表達(dá)式會(huì)失?。↗avaScript認(rèn)為這里不止一個(gè)字符),charAt方法無法返回單個(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)格說是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)為原來的編碼
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') // "你好"
- JS實(shí)現(xiàn)對(duì)中文字符串進(jìn)行utf-8的Base64編碼的方法(使其與Java編碼相同)
- js中字符串編碼函數(shù)escape()、encodeURI()、encodeURIComponent()區(qū)別詳解
- JavaScript中字符串與Unicode編碼互相轉(zhuǎn)換的實(shí)現(xiàn)方法
- JS對(duì)字符串編碼的幾種方式使用指南
- JavaScript將字符串轉(zhuǎn)換成字符編碼列表的方法
- JavaScript中最簡(jiǎn)潔的編碼html字符串的方法
- Python中還原JavaScript的escape函數(shù)編碼后字符串的方法
- js實(shí)現(xiàn)字符串的16進(jìn)制編碼不加密
- js對(duì)圖片base64編碼字符串進(jìn)行解碼并輸出圖像示例
- 將字符串轉(zhuǎn)換成gb2312或者utf-8編碼的參數(shù)(js版)
- 對(duì)字符串進(jìn)行HTML編碼和解碼的JavaScript函數(shù)
- JS 文字符串轉(zhuǎn)換unicode編碼函數(shù)
- JS對(duì)URL字符串進(jìn)行編碼/解碼分析
相關(guān)文章
Javascript之BOM(window對(duì)象)詳解
下面小編就為大家?guī)硪黄狫avascript之BOM(window對(duì)象)詳解。小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-05-05Js刪除數(shù)組中某一項(xiàng)或幾項(xiàng)的幾種方法(推薦)
下面小編就為大家?guī)硪黄狫s刪除數(shù)組中某一項(xiàng)或幾項(xiàng)的幾種方法(推薦)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-07-07JavaScript對(duì)內(nèi)存分配及管理機(jī)制詳細(xì)解析
本文主要講述了JavaScript的垃圾回收原理和具體的過程。需要的朋友可以過來參考下,希望對(duì)大家有所幫助2013-11-11