JavaScript 字符串

2018-07-24 11:49 更新

目錄

概述

定義

字符串就是零個(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)只能寫(xiě)在一行內(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"

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

連接運(yùn)算符(+)可以連接多個(gè)單行字符串,將長(zhǎng)字符串拆成多行書(shū)寫(xiě),輸出的時(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)表示一些特殊字符,所以又稱(chēng)為轉(zhuǎn)義符。

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

  • \0 null(\u0000)
  • \b 后退鍵(\u0008)
  • \f 換頁(yè)符(\u000C)
  • \n 換行符(\u000A)
  • \r 回車(chē)鍵(\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ù)(000377),代表一個(gè)字符。HHH對(duì)應(yīng)該字符的Unicode碼點(diǎn),比如\251表示版權(quán)符號(hào)。顯然,這種方法只能輸出256種字符。

(2)\xHH

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

(3)\uXXXX

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

下面是這三種字符特殊寫(xiě)法的例子。

'\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ù)組的相似性?xún)H此而已。實(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ì)默默地失敗。

字符串也無(wú)法直接使用數(shù)組的方法,必須通過(guò)call方法間接使用。

var s = 'hello';

s.join(' ') // TypeError: s.join is not a function

Array.prototype.join.call(s, ' ') // "h e l l o"

上面代碼中,如果直接對(duì)字符串使用數(shù)組的join方法,會(huì)報(bào)錯(cuò)不存在該方法。但是,可以通過(guò)call方法,間接對(duì)字符串使用join方法。

不過(guò),由于字符串是只讀的,那些會(huì)改變?cè)瓟?shù)組的方法,比如push()sort()、reverse()splice()都對(duì)字符串無(wú)效,只有將字符串顯式轉(zhuǎn)為數(shù)組后才能使用,參見(jiàn)《標(biāo)準(zhǔn)庫(kù)》一章的數(shù)組部分。

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不僅以Unicode儲(chǔ)存字符,還允許直接在程序中使用Unicode編號(hào)表示字符,即將字符寫(xiě)成\uxxxx的形式,其中xxxx代表該字符的Unicode編碼。比如,\u00A9代表版權(quán)符號(hào)。

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

解析代碼的時(shí)候,JavaScript會(huì)自動(dòng)識(shí)別一個(gè)字符是字面形式表示,還是Unicode形式表示。輸出給用戶(hù)的時(shí)候,所有字符都會(huì)轉(zhuǎn)成字面形式。

var f\u006F\u006F = 'abc';
foo // "abc"

上面代碼中,第一行的變量名foo是Unicode形式表示,第二行是字面形式表示。JavaScript會(huì)自動(dòng)識(shí)別。

我們還需要知道,每個(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+0000U+FFFF之間的字符,長(zhǎng)度為16位(即2個(gè)字節(jié));對(duì)于U+10000U+10FFFF之間的字符,長(zhǎng)度為32位(即4個(gè)字節(jié)),而且前兩個(gè)字節(jié)在0xD8000xDBFF之間,后兩個(gè)字節(jié)在0xDC000xDFFF之間。舉例來(lái)說(shuō),U+1D306對(duì)應(yīng)的字符為????,它寫(xiě)成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+10000U+10FFFF之間的字符,JavaScript總是視為兩個(gè)字符(字符的length屬性為2),用來(lái)匹配單個(gè)字符的正則表達(dá)式會(huì)失?。↗avaScript認(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ù)遇到這類(lèi)字符,都會(huì)產(chǎn)生錯(cuò)誤的結(jié)果(詳見(jiàn)《標(biāo)準(zhǔn)庫(kù)》一章的String對(duì)象章節(jié))。如果要完成字符串相關(guān)操作,就必須判斷字符是否落在0xD8000xDFFF這個(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)等其他字符串操作,都必須做類(lèi)似的處理。

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') // "你好"

參考鏈接

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)