数据类型 字符串
引号(Quotes)
单引号和双引号基本相同。但是,反引号允许我们通过 ${…} 将任何表达式嵌入到字符串中:
function sum(a, b) {
return a + b;
}
alert(`1 + 2 = ${sum(1, 2)}.`); // 1 + 2 = 3.
使用反引号的另一个优点是它们允许字符串跨行:
let guestList = `Guests:
* John
* Pete
* Mary
`;
alert(guestList); // 客人清单,多行
特殊字符
字符 描述
\n 换行
\r 回车:不单独使用。Windows 文本文件使用两个字符 \r\n 的组合来表示换行。
\', \" 引号
\\ 反斜线
\t 制表符
\b, \f, \v 退格,换页,垂直标签 —— 为了兼容性,现在已经不使用了。
\xXX 具有给定十六进制 Unicode XX 的 Unicode 字符,例如:'\x7A' 和 'z' 相同。
\uXXXX 以 UTF-16 编码的十六进制代码 XXXX 的 unicode 字符,例如 \u00A9 —— 是版权符号 © 的 unicode。它必须正好是 4 个十六进制数字。
\u{X…XXXXXX}(1 到 6 个十六进制字符) 具有给定 UTF-32 编码的 unicode 符号。一些罕见的字符用两个 unicode 符号编码,占用 4 个字节。这样我们就可以插入长代码了。
所有的特殊字符都以反斜杠字符 \ 开始。它也被称为“转义字符”。
字符串长度
length 属性表示字符串长度:
alert( `My\n`.length ); // 3
注意 \n 是一个单独的“特殊”字符,所以长度确实是 3。
length 是一个属性
掌握其他编程语言的人,有时会错误地调用 str.length() 而不是 str.length。这是行不通的。
请注意 str.length 是一个数字属性,而不是函数。后面不需要添加括号。
访问字符
要获取在 pos 位置的一个字符,可以使用方括号 [pos] 或者调用 str.charAt(pos) 方法。第一个字符从零位置开始:
let str = `Hello`;
// 第一个字符
alert( str[0] ); // H
alert( str.charAt(0) ); // H
// 最后一个字符
alert( str[str.length - 1] ); // o
方括号是获取字符的一种现代化方法,而 charAt 是历史原因才存在的。
它们之间的唯一区别是,如果没有找到字符,[] 返回 undefined,而 charAt 返回一个空字符串:
我们也可以使用 for..of 遍历字符:
for (let char of "Hello") {
alert(char); // H,e,l,l,o(char 变为 "H",然后是 "e",然后是 "l" 等)
}
字符串是不可变的
在 JavaScript 中,字符串不可更改。改变字符是不可能的。
我们证明一下为什么不可能:
let str = 'Hi';
str[0] = 'h'; // error
alert( str[0] ); // 无法运行
通常的解决方法是创建一个新的字符串,并将其分配给 str 而不是以前的字符串。
例如:
let str = 'Hi';
str = 'h' + str[1]; // 替换字符串
alert( str ); // hi
改变大小写
toLowerCase() 和 toUpperCase() 方法可以改变大小写:
alert( 'Interface'.toUpperCase() ); // INTERFACE
alert( 'Interface'.toLowerCase() ); // interface
或者我们想要使一个字符变成小写:
alert( 'Interface'[0].toLowerCase() ); // 'i'
查找子字符串
在字符串中查找子字符串有很多种方法。
str.indexOf
第一个方法是 str.indexOf(substr, pos)。
它从给定位置 pos 开始,在 str 中查找 substr,如果没有找到,则返回 -1,否则返回匹配成功的位置。
例如:
let str = 'Widget with id';
alert( str.indexOf('Widget') ); // 0,因为 'Widget' 一开始就被找到
alert( str.indexOf('widget') ); // -1,没有找到,检索是大小写敏感的
alert( str.indexOf("id") ); // 1,"id" 在位置 1 处(……idget 和 id)
可选的第二个参数允许我们从给定的起始位置开始检索。
例如,"id" 第一次出现的位置是 1。查询下一个存在位置时,我们从 2 开始检索:
let str = 'Widget with id';
alert( str.indexOf('id', 2) ) // 12
如果我们对所有存在位置都感兴趣,可以在一个循环中使用 indexOf。每一次新的调用都发生在上一匹配位置之后:
相同的算法可以简写:
let str = "As sly as a fox, as strong as an ox";
let target = "as";
let pos = -1;
while ((pos = str.indexOf(target, pos + 1)) != -1) {
alert( pos );
}
str.lastIndexOf(substr, pos)
还有一个类似的方法 str.lastIndexOf(substr, position),它从字符串的末尾开始搜索到开头。
它会以相反的顺序列出这些事件。
在 if 测试中 indexOf 有一点不方便。我们不能像这样把它放在 if 中:
let str = "Widget with id";
if (str.indexOf("Widget")) {
alert("We found it"); // 不工作!
}
上述示例中的 alert 不会显示,因为 str.indexOf("Widget") 返回 0(意思是它在起始位置就查找到了匹配项)。是的,但是 if 认为 0 表示 false。
因此我们应该检查 -1,像这样:
let str = "Widget with id";
if (str.indexOf("Widget") != -1) {
alert("We found it"); // 现在工作了!
}
includes,startsWith,endsWith
更现代的方法 str.includes(substr, pos) 根据 str 中是否包含 substr 来返回 true/false。
如果我们需要检测匹配,但不需要它的位置,那么这是正确的选择:
alert( "Widget with id".includes("Widget") ); // true
alert( "Hello".includes("Bye") ); // false
str.includes 的第二个可选参数是开始搜索的起始位置:
alert( "Midget".includes("id") ); // true
alert( "Midget".includes("id", 3) ); // false, 从位置 3 开始没有 "id"
方法 str.startsWith 和 str.endsWith 的功能与其名称所表示的意思相同:
alert( "Widget".startsWith("Wid") ); // true,"Widget" 以 "Wid" 开始
alert( "Widget".endsWith("get") ); // true,"Widget" 以 "get" 结束
获取子字符串
JavaScript 中有三种获取字符串的方法:substring、substr 和 slice。
str.slice(start [, end])
返回字符串从 start 到(但不包括)end 的部分。
例如:
let str = "stringify";
alert( str.slice(0, 5) ); // 'strin',从 0 到 5 的子字符串(不包括 5)
alert( str.slice(0, 1) ); // 's',从 0 到 1,但不包括 1,所以只有在 0 处的字符
如果没有第二个参数,slice 会一直运行到字符串末尾:
let str = "stringify";
alert( str.slice(2) ); // 从第二个位置直到结束
start/end 也有可能是负值。它们的意思是起始位置从字符串结尾计算:
let str = "stringify";
// 从右边的第四个位置开始,在右边的第一个位置结束
alert( str.slice(-4, -1) ); // 'gif'
str.substring(start [, end])
返回字符串在 start 和 end 之间 的部分。
这与 slice 几乎相同,但它允许 start 大于 end。
例如:
let str = "stringify";
// 这些对于 substring 是相同的
alert( str.substring(2, 6) ); // "ring"
alert( str.substring(6, 2) ); // "ring"
// ……但对 slice 是不同的:
alert( str.slice(2, 6) ); // "ring"(一样)
alert( str.slice(6, 2) ); // ""(空字符串)
不支持负参数(不像 slice),它们被视为 0。
str.substr(start [, length])
返回字符串从 start 开始的给定 length 的部分。
与以前的方法相比,这个允许我们指定 length 而不是结束位置:
let str = "stringify";
alert( str.substr(2, 4) ); // 'ring',从位置 2 开始,获取 4 个字符
第一个参数可能是负数,从结尾算起:
let str = "stringify";
alert( str.substr(-4, 2) ); // 'gi',从第 4 位获取 2 个字符
我们回顾一下这些方法,以免混淆:
方法 选择方式…… 负值参数
slice(start, end) 从 start 到 end(不含 end) 允许
substring(start, end) start 与 end 之间(包括 start,但不包括 end) 负值代表 0
substr(start, length) 从 start 开始获取长为 length 的字符串 允许 start 为负数
比较字符串
小写字母总是大于大写字母:
str.codePointAt(pos)
返回在 pos 位置的字符代码 :
// 不同的字母有不同的代码
alert( "z".codePointAt(0) ); // 122
alert( "Z".codePointAt(0) ); // 90
String.fromCodePoint(code)
通过数字 code 创建字符
alert( String.fromCodePoint(90) ); // Z
正确的比较
调用 str.localeCompare(str2) 会根据语言规则返回一个整数,这个整数能指示字符串 str 在排序顺序中排在字符串 str2 前面、后面、还是相同:
如果 str 排在 str2 前面,则返回负数。
如果 str 排在 str2 后面,则返回正数。
如果它们在相同位置,则返回 0。
例如:
alert( 'Österreich'.localeCompare('Zealand') ); // -1
总结
有 3 种类型的引号。反引号允许字符串跨越多行并可以使用 ${…} 在字符串中嵌入表达式。
JavaScript 中的字符串使用的是 UTF-16 编码。
我们可以使用像 \n 这样的特殊字符或通过使用 \u... 来操作它们的 unicode 进行字符插入。
获取字符时,使用 []。
获取子字符串,使用 slice 或 substring。
字符串的大/小写转换,使用:toLowerCase/toUpperCase。
查找子字符串时,使用 indexOf 或 includes/startsWith/endsWith 进行简单检查。
根据语言比较字符串时使用 localeCompare,否则将按字符代码进行比较。
还有其他几种有用的字符串方法:
str.trim() —— 删除字符串前后的空格 (“trims”)。
str.repeat(n) —— 重复字符串 n 次。