字符串是 不可变的 JavaScript 字符序列。每个字符都是一个 16 位 UTF-16 代码单元。这意味着一个 Unicode 字符由一个或两个 JavaScript 字符表示。每当您计算字符数或拆分字符串时,您主要需要注意双字符情况(请参阅第 24 章)。
单引号和双引号都可以用来 界定字符串字面量:
'He said: "Hello"'
"He said: \"Hello\""
'Everyone\'s a winner'
"Everyone's a winner"
因此,您可以自由使用任何一种引号。但是,有一些注意事项
如果您的代码一致地使用引号,它将看起来更干净。但有时,不同的引号意味着您不必转义,这可以证明您不那么一致是合理的(例如,您通常可能使用单引号,但暂时切换到双引号来编写前面最后一个示例)。
字符串字面量中的大多数字符 只代表它们自己。反斜杠用于 转义,并启用了几种特殊功能:
您可以 通过使用反斜杠转义行尾(行终止符,行终止符)将字符串扩展到多行:
var
str
=
'written \
over \
multiple \
lines'
;
console
.
log
(
str
===
'written over multiple lines'
);
// true
另一种方法是使用 加号运算符进行连接:
var
str
=
'written '
+
'over '
+
'multiple '
+
'lines'
;
\b
是退格符,\f
是换页符,\n
是换行符,\r
是回车符,\t
是水平制表符,\v
是垂直制表符。表示自身的转义字符:\'
是单引号,\"
是双引号,\\
是反斜杠。除 b f n r t v x u
和十进制数字以外的所有字符也代表它们自己。以下是两个例子
> '\"' '"' > '\q' 'q'
\0
表示。
\xHH
(HH
是两个十六进制数字) 通过 ASCII 代码指定一个字符。例如:
> '\x4D' 'M'
\uHHHH
(HHHH
是四个十六进制数字) 指定一个 UTF-16 代码单元(请参阅第 24 章)。以下是两个例子
> '\u004D' 'M' > '\u03C0' 'π'
有两个操作 返回字符串的第 n 个字符。[16] 请注意,JavaScript 没有用于字符的特殊数据类型;这些操作返回字符串
> 'abc'.charAt(1) 'b' > 'abc'[1] 'b'
一些较旧的浏览器不支持通过方括号进行类似数组的字符访问。
值 | 结果 |
|
|
|
|
布尔值 |
|
| |
数字 | 数字作为字符串(例如, |
字符串 | 与输入相同(无需转换) |
对象 | 调用 |
| (作为函数调用,而不是作为构造函数调用) |
| |
| (不适用于 |
我更喜欢 String()
,因为它更具描述性。以下是一些例子
> String(false) 'false' > String(7.35) '7.35' > String({ first: 'John', last: 'Doe' }) '[object Object]' > String([ 'a', 'b', 'c' ]) 'a,b,c'
请注意,对于显示数据,JSON.stringify()
(JSON.stringify(value, replacer?, space?)) 通常 比规范的字符串转换效果更好:
> console.log(JSON.stringify({ first: 'John', last: 'Doe' })) {"first":"John","last":"Doe"} > console.log(JSON.stringify([ 'a', 'b', 'c' ])) ["a","b","c"]
当然,您必须了解 JSON.stringify()
的局限性——它并不总是显示所有内容。例如,它隐藏了其值无法处理的属性(函数等等!)。从好的方面来说,它的输出可以由 eval()
解析,并且它可以将深度嵌套的数据显示为格式良好的树。
考虑到 JavaScript 自动转换的频率,转换并不总是可逆的,这很遗憾,尤其是在布尔值方面:
> String(false) 'false' > Boolean('false') true
对于 undefined
和 null
,我们也面临着类似的问题。
有两种比较字符串的方法。 首先,您可以使用比较运算符:<
、>
、===
、<=
、>=
。 它们有以下缺点:
它们区分大小写
> 'B' > 'A' // ok true > 'B' > 'a' // should be true false
它们不能很好地处理变音符号和重音符号
> 'ä' < 'b' // should be true false > 'é' < 'f' // should be true false
其次,您可以使用 String.prototype.localeCompare(other)
,它往往表现更好,但并不总是受支持的(有关详细信息,请参阅搜索和比较)。以下是 Firefox 控制台中的交互
> 'B'.localeCompare('A') 2 > 'B'.localeCompare('a') 2 > 'ä'.localeCompare('b') -2 > 'é'.localeCompare('f') -2
小于零的结果意味着接收者“小于”参数。大于零的结果意味着接收者“大于”参数。
只要其操作数之一是字符串,运算符 +
就会执行字符串连接。 如果您想在变量中收集字符串片段,则复合赋值运算符 +=
很有用:
> var str = ''; > str += 'Say hello '; > str += 7; > str += ' times fast!'; > str 'Say hello 7 times fast!'
看起来每当将一个片段添加到 str
时,前面的方法都会创建一个新的字符串。较旧的 JavaScript 引擎就是这样做的,这意味着您可以通过首先将所有片段收集在一个数组中,然后在最后一步将它们连接起来,从而提高字符串连接的性能:
> var arr = []; > arr.push('Say hello '); > arr.push(7); > arr.push(' times fast'); > arr.join('') 'Say hello 7 times fast'
但是,较新的引擎通过 +
优化字符串连接,并在内部使用类似的方法。 因此,加号运算符在这些引擎上速度更快。
前一种调用方式比较常见。
String.fromCharCode(codeUnit1, codeUnit2, ...)
生成一个字符串,其字符是由 16 位无符号整数 codeUnit1
、codeUnit2
等指定的 UTF-16 代码单元。 例如:
> String.fromCharCode(97, 98, 99) 'abc'
如果要将数字数组转换为字符串,可以通过 apply()
来实现(请参阅 func.apply(thisValue, argArray))
> String.fromCharCode.apply(null, [97, 98, 99]) 'abc'
原始字符串的所有方法都存储在 String.prototype
中(请参阅原始值从包装器借用其方法)。接下来,我将描述它们如何处理原始字符串,而不是 String
的实例。
以下方法从接收器中提取子字符串
String.prototype.charAt(pos)
返回一个字符串,其中包含位置 pos
处的字符。 例如:
> 'abc'.charAt(1) 'b'
以下两个表达式返回相同的结果,但一些较旧的 JavaScript 引擎仅支持 charAt()
来访问字符
str
.
charAt
(
n
)
str
[
n
]
String.prototype.charCodeAt(pos)
返回位置 pos
处的 JavaScript 字符(UTF-16 代码单元;请参阅 第 24 章)的代码(16 位无符号整数)。
> 'abc'.split('').map(function (x) { return x.charCodeAt(0) }) [ 97, 98, 99 ]
charCodeAt()
的反函数是 String.fromCharCode()
。
String.prototype.slice(start, end?)
返回从位置 start
开始到位置 end
(不包括)的子字符串。 这两个参数都可以是负数,然后将字符串的 length
添加到它们中:
> 'abc'.slice(2) 'c' > 'abc'.slice(1, 2) 'b' > 'abc'.slice(-2) 'bc'
String.prototype.substring(start, end?)
slice()
,它类似,但可以处理负数位置,并且在不同浏览器中的实现更加一致。String.prototype.split(separator?, limit?)
提取由 separator
分隔的接收器的子字符串,并将它们返回到一个数组中。该方法有两个参数:
separator
:字符串或正则表达式。如果缺少,则返回完整的字符串,并包装在一个数组中。limit
:如果给出,则返回的数组最多包含 limit
个元素。以下是一些例子
> 'a, b,c, d'.split(',') // string [ 'a', ' b', 'c', ' d' ] > 'a, b,c, d'.split(/,/) // simple regular expression [ 'a', ' b', 'c', ' d' ] > 'a, b,c, d'.split(/, */) // more complex regular expression [ 'a', 'b', 'c', 'd' ] > 'a, b,c, d'.split(/, */, 2) // setting a limit [ 'a', 'b' ] > 'test'.split() // no separator provided [ 'test' ]
如果有一个组,则匹配项也会作为数组元素返回
> 'a, b , '.split(/(,)/) [ 'a', ',', ' b ', ',', ' ' ] > 'a, b , '.split(/ *(,) */) [ 'a', ',', 'b', ',', '' ]
使用 ''
(空字符串)作为分隔符来生成一个包含字符串字符的数组
> 'abc'.split('') [ 'a', 'b', 'c' ]
上一节是关于提取子字符串,而本节是关于将给定的字符串转换为新的字符串。 这些方法的典型用法如下:
var
str
=
str
.
trim
();
换句话说,原始字符串在被(非破坏性地)转换后就被丢弃
String.prototype.trim()
> '\r\nabc \t'.trim() 'abc'
String.prototype.concat(str1?, str2?, ...)
> 'hello'.concat(' ', 'world', '!') 'hello world!'
String.prototype.toLowerCase()
> 'MJÖLNIR'.toLowerCase() 'mjölnir'
String.prototype.toLocaleLowerCase()
toLowerCase()
的工作方式相同,但遵循当前语言环境的规则。根据 ECMAScript 规范:“只有在少数情况下(例如土耳其语),该语言的规则与常规 Unicode 大小写映射冲突时,才会存在差异。”String.prototype.toUpperCase()
> 'mjölnir'.toUpperCase() 'MJÖLNIR'
String.prototype.toLocaleUpperCase()
toUpperCase()
的工作方式相同,但遵循当前语言环境的规则。String.prototype.indexOf(searchString, position?)
从 position
(默认为 0)开始搜索 searchString
。它返回找到 searchString
的位置,如果找不到则返回 -1
> 'aXaX'.indexOf('X') 1 > 'aXaX'.indexOf('X', 2) 3
请注意,在字符串中查找文本时,正则表达式同样有效。例如,以下两个表达式是等效的:
str
.
indexOf
(
'abc'
)
>=
0
/
abc
/
.
test
(
str
)
String.prototype.lastIndexOf(searchString, position?)
从 position
(默认为结尾)开始向后搜索 searchString
。它返回找到 searchString
的位置,如果找不到则返回 -1:
> 'aXaX'.lastIndexOf('X') 3 > 'aXaX'.lastIndexOf('X', 2) 1
String.prototype.localeCompare(other)
对字符串与 other
执行区分区域设置的比较。它返回一个数字:
other
之前other
other
之后例如
> 'apple'.localeCompare('banana') -2 > 'apple'.localeCompare('apple') 0
并非所有 JavaScript 引擎都能正确实现此方法。有些只是将其基于比较运算符。但是,ECMAScript 国际化 API(请参阅ECMAScript 国际化 API)确实提供了一个支持 Unicode 的实现。也就是说,如果该 API 在引擎中可用,则 localeCompare()
将起作用。
如果支持,则 localeCompare()
是比比较运算符更好的比较字符串的选择。有关更多信息,请参阅比较字符串。
以下方法使用正则表达式
String.prototype.search(regexp)
(在String.prototype.search:匹配项位于哪个索引?中有更详细的说明)返回 regexp
在接收器中匹配的第一个索引(如果没有匹配项,则返回 -1):
> '-yy-xxx-y-'.search(/x+/) 4
String.prototype.match(regexp)
(在String.prototype.match:捕获组或返回所有匹配的子字符串中有更详细的说明)将给定的正则表达式与接收器进行匹配。如果未设置 regexp
的标志 /g
,则它将返回第一个匹配项的匹配对象:
> '-abb--aaab-'.match(/(a+)b/) [ 'ab', 'a', index: 1, input: '-abb--aaab-' ]
如果设置了标志 /g
,则所有完整匹配项(组 0)都将返回到数组中
> '-abb--aaab-'.match(/(a+)b/g) [ 'ab', 'aaab' ]
String.prototype.replace(search, replacement)
(在 String.prototype.replace:搜索和替换中有更详细的说明)搜索 search
并将其替换为 replacement
。 search
可以是字符串或正则表达式,replacement
可以是字符串或函数。除非您使用正则表达式作为 search
,并且其标志 /g
已设置,否则只会替换第一个匹配项
> 'iixxxixx'.replace('i', 'o') 'oixxxixx' > 'iixxxixx'.replace(/i/, 'o') 'oixxxixx' > 'iixxxixx'.replace(/i/g, 'o') 'ooxxxoxx'
替换字符串中的美元符号 ($
) 允许您引用完整匹配项或捕获组:
> 'iixxxixx'.replace(/i+/g, '($&)') // complete match '(ii)xxx(i)xx' > 'iixxxixx'.replace(/(i+)/g, '($1)') // group 1 '(ii)xxx(i)xx'
您还可以通过函数计算替换
> function repl(all) { return '('+all.toUpperCase()+')' } > 'axbbyyxaa'.replace(/a+|b+/g, repl) '(A)x(BB)yyx(AA)'