概述
定义
字符串就是零个或者多个排在一起的字符,放在单引号或者双引号之中。
'abc'
"abc"
单引号字符串的内部,可以使用双引号。双引号的内部可以使用单引号。
'key = "value"'
"It's a long journey"
上面两个都是合法字符串。
如果要在单引号字符串的内部,使用单引号(或者在双引号的内部使用双引号),就必须在内部的单引号(或者双引号)前面加上反斜杠,用来转义。
'Did she say \'hello\'?'
// "Did she say 'Hello'?"
"Did she say \"Hello\"?"
// "Did she say "Hello"?"
由于HTML语言的属性值使用双引号,所以很多项目约定JavaScript语言的字符串只使用单引号。
字符串默认只能写在一行内,分成多行就会报错。
'a
b
c'
// SyntaxError: Unexpected token ILLEGAL
上面代码将一个字符串分成三行,JavaScript就会报错。
如果字符串必须分成多行,可以在每一行的尾部使用反斜杠。
var longString = "Long \
long \
long \
string"
// "Long long long string"
上面代码表示,加了反斜杠以后,原来写在一行的字符串,可以分成多行书写。但是,输出的时候还是单行,效果与写在同一行完全一样。注意反斜杠后面必须是换行符,而不能有其他字符(比如空格),否则会报错。
连接运算符(+)可以连接多个字符串,将长字符串拆成多行书写,输出的时候也是单行。
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"
上面的例子中,输出的字符串就会多行。
转义
反斜杠()在字符串内有特殊含义,用来表示一些特殊的字符,所以又称为转义字符。
需要用反斜杠转义的特殊字符,主要有一下这些:
\0 null(\u0000)
\b 后退键(\u0008)
\f 换页符(\u000C)
\n 换行符(\u000A)
\r 回车键(\u000D)
\t 制表符(\u0009)
\v 垂直制表符(\u000B)
\’ 单引号(\u0027)
\” 双引号(\u0022)
\反斜杠(\u005C)
上面这些字符前面加上反斜杠,都表示特殊含义。
console.log('1\n2')
//1
//2
上面代码中,\n表示换行,输出的时候就分成了两行。
反斜杠还有三种特殊的用法。
(1)\HHH
反斜杠后面紧跟三个八进制数(000到377),代表一个字符。HHH对应该字符的Unicode码点,比如\251表示版权符号。显然,这种方法只能输出256中字符。
(2)\xHH
\x后面紧跟两个十六进制数(00到FF),代表一个字符。HH对应该字符的Unicode码点,比如\xA9表示版权符号。这种方法也只能输出256种字符。
(3) \uXXXX
\u后面紧跟四个十六进制数(0000到FFFF),代表一个字符。HHHH对应该字符的Unicode码点,比如\u00A9表示版权符号。
下面这是三种字符特殊写法的例子。
'\251' // "©"
'\xA9' // "©"
'\u00A9' // "©"
'\172' === 'z' // true
'\x7A' === 'z' // true
'\u007A' === 'z' // true
如果在非特殊字符前面使用反斜杠,则反斜杠会被省略。
'\a'
// "a"
上面代码中,a是一个正常字符,前面加反斜杠没有特殊含义,反斜杠会被自动省略。
如果字符串的正常内容之中,需要包含反斜杠,则反斜杠前面需要再加一个反斜杠,用来对自身转义。
"Prev \\ Next"
// "Prev \ Next"
字符串与数组
字符串可以被视为字符串数组,因此可以使用数组的方括号运算符,用来返回某个位置的字符(位置编号从0开始)。
var s = 'hello';
s[0] // "h"
s[1] // "e"
s[4] // "o"
//直接对字符串使用方括号运算符
'hello'[1] // "e"
如果方括号中的数组超过字符串的长度,或者方括号中根本不是数字,则返回unefined。
'abc'[3] // undefined
'abc'[-1] //undefined
'abc'.['x'] //undefined
但是,字符串与数组的相似性仅此而已。实际上,无法改变字符串之中的额单个字符。
var s = 'hello';
delete s[0];
s //"hello"
s[1] = 'a';
s //"hello"
s[5] = '!'
s //"hello"
上面代码表示,字符串内部单个字符无法改变赫尔增删,这些操作会陌陌失败。
字符串也无法直接使用数组的方法,必须通过call方法简介使用。
var s = 'hello';
s.join(' ') / TypeError: s.join is not a function
Array.prototype.join.call(s,' ') // "h e l l o"
上面代码中,如果直接对字符串使用数组的join方法,就会报错不存在该方法。但是,可以通过call方法,间接对字符串使用join方法。
不过,由于字符串是只读的,那些会改变原数组的方法,比如push()、sort()、reverse()、splice()都对字符串无效,只有将字符串显式地转为数组后才能使用。
length属性
length属性返回字符串的长度,该属性也是无法改变的。
var s = 'hello';
s.lengh // 5
s.length = 3;
s.length // 5
s.length = 7
s.length //5
上面代码表示字符串的length属性无法改变,但是不会报错。
字符集
JavaScript使用Unicode字符集。也就是说,在JavaScript引擎内部,所有字符都用Unicode表示。
JavaScript不仅以Unicode存储字符,还允许直接在程序中使用Unicode编号表示字符,即字符写成\uxxxx形式,其中xxxx表示该字符Unicode编码。比如,\u00A9代表版权符号。
var s = '\u00A9';
s //"©"
解析代码的时候,第一行变量名foo是Unicode形式表示,第二行是字面形式表示。JavaScript会自动识别。
我们还需要知道,每个字符在JavaScript内部都是以16位(即两个字节)的UTF-16格式存储。也就是说,JavaScript的单位字符长度固定为16位字符,即2个字节。
但是,UTF-16有两种长度:对于U+0000到U+FFFF之间的字符,长度为16位(即2个字节);对于U+10000到U+10FFFF之间的字符,长度为32位(即4个字节)而且前两个字节在0xD800到0xDBFF之间,后两个字节在0xDC00到0xDFFF之间。举例来说,U+1D306对应的字符为��,它写成UTF-16就是0xD834 0xDF06。浏览器会正确将这四个字节识别为一个字符,但是JavaScript内部的字符长度总是固定为16位,会把这四个字节视为两个字符。
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
上面代码说明,对于于U+10000到U+10FFFF之间的字符,JavaScript总是视为两个字符(字符的length属性为2),用来匹配单个字符的正则表达式会失败(JavaScript认为这里不止一个字符),charAt方法无法返回单个字符,charCodeAt方法返回每个字节对应的十进制值。
所以处理的时候,必须把这一点考虑在内。对于4个字节的Unicode字符,假定C是字符的Unicode编号,H是前两个字节,L是后两个字节,则它们之间的换算关系如下。
// 将大于U+FFFF的字符,从Unicode转为UTF-16
H = Math.floor((C - 0x10000) / 0x400) + 0xD800
L = (C - 0x10000) % 0x400 + 0xDC00
// 将大于U+FFFF的字符,从UTF-16转为Unicode
C = (H - 0xD800) * 0x400 + L - 0xDC00 + 0x10000
下面的正则表达式可以识别所有UTF-16字符。
([\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF])
由于JavaScript引擎(严格说是ES5规格)不能自动识别辅助平面(编号大于0xFFFF)的Unicode字符,导致所有字符串处理函数遇到这类字符,都会产生错误的结果(详见《标准库》一章的String对象章节)。如果要完成字符串相关操作,就必须判断字符是否落在0xD800到0xDFFF这个区间。
下面是能够正确处理字符串遍历的函数。
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转码
Base64是一种编码方法,可以将任意字符转成可打印字符。使用这种编码方法,主要不是为了加密,而是为了不出现特殊字符,简化程序的处理。
JavaScript原生提供两个Base64相关方法。
btoa():字符串或二进制值转为Base64编码
atob():Base64编码转为原来的编码
var string = 'Hello World!';
btoa(string) // "SGVsbG8gV29ybGQh"
atob('SGVsbG8gV29ybGQh') // "Hello World!"
这两个方法不适合非ASCII码的字符,会报错。
btoa('你好')
// Uncaught DOMException: The string to be encoded contains characters outside of the Latin1 range.
要将非ASCII码字符转为Base64编码,必须中间插入一个转码环节,再使用这两个方法。
function b64Encode(str) {
return btoa(encodeURIComponent(str));
}
function b64Decode(str) {
return decodeURIComponent(atob(str));
}
b64Encode('你好') // "JUU0JUJEJUEwJUU1JUE1JUJE"
b64Decode('JUU0JUJEJUEwJUU1JUE1JUJE') // "你好"