字符串
String
对象用于表示和操作一系列字符。
描述
字符串可用于保存可以用文本形式表示的数据。对字符串最常用的操作包括检查其length
、使用+
和 +=
字符串运算符构建和连接它们、使用indexOf()
方法检查子字符串的存在或位置,或使用substring()
方法提取子字符串。
创建字符串
字符串可以作为基本类型创建,来自字符串字面量,或者作为对象,使用String()
构造函数
const string1 = "A string primitive";
const string2 = 'Also a string primitive';
const string3 = `Yet another string primitive`;
const string4 = new String("A String object");
字符串基本类型和字符串对象共享许多行为,但也有其他重要的差异和注意事项。请参见下面的“字符串基本类型和字符串对象”。
字符串字面量可以使用单引号或双引号指定,它们被视为相同,或者使用反引号字符`。后一种形式指定了一个模板字面量:使用这种形式,您可以插入表达式。有关字符串字面量语法的更多信息,请参见词法语法。
字符访问
有两种方法可以访问字符串中的单个字符。第一种是charAt()
方法
"cat".charAt(1); // gives value "a"
另一种方法是将字符串视为类似数组的对象,其中单个字符对应于数值索引
"cat"[1]; // gives value "a"
当使用方括号表示法访问字符时,尝试删除或为这些属性赋值将不会成功。所涉及的属性既不可写也不可配置。(有关更多信息,请参见Object.defineProperty()
。)
比较字符串
使用小于和大于运算符比较字符串
const a = "a";
const b = "b";
if (a < b) {
// true
console.log(`${a} is less than ${b}`);
} else if (a > b) {
console.log(`${a} is greater than ${b}`);
} else {
console.log(`${a} and ${b} are equal.`);
}
请注意,所有比较运算符,包括===
和 ==
,都区分大小写地比较字符串。不区分大小写地比较字符串的一种常见方法是在比较之前将两者转换为相同的大小写(大写或小写)。
function areEqualCaseInsensitive(str1, str2) {
return str1.toUpperCase() === str2.toUpperCase();
}
选择是通过toUpperCase()
还是toLowerCase()
进行转换基本上是任意的,并且在扩展到拉丁字母之外时,两者都不是完全健壮的。例如,德语小写字母ß
和 ss
都被 toUpperCase()
转换为 SS
,而土耳其字母 ı
会被 toLowerCase()
错误地报告为与 I
不相等,除非专门使用toLocaleLowerCase("tr")
。
const areEqualInUpperCase = (str1, str2) =>
str1.toUpperCase() === str2.toUpperCase();
const areEqualInLowerCase = (str1, str2) =>
str1.toLowerCase() === str2.toLowerCase();
areEqualInUpperCase("ß", "ss"); // true; should be false
areEqualInLowerCase("ı", "I"); // false; should be true
测试不区分大小写的相等性的区域感知且稳健的解决方案是使用Intl.Collator
API 或字符串的localeCompare()
方法——它们共享相同的接口——并将sensitivity
选项设置为 "accent"
或 "base"
。
const areEqual = (str1, str2, locale = "en-US") =>
str1.localeCompare(str2, locale, { sensitivity: "accent" }) === 0;
areEqual("ß", "ss", "de"); // false
areEqual("ı", "I", "tr"); // true
localeCompare()
方法能够以类似于 strcmp()
的方式进行字符串比较——它允许以区域感知的方式对字符串进行排序。
字符串原始值和字符串对象
请注意,JavaScript 区分 String
对象和原始字符串 值。(Boolean
和Numbers
也是如此。)
字符串字面量(用双引号或单引号表示)以及在非构造函数上下文中从 String
调用返回的字符串(即,在不使用new
关键字的情况下调用)都是原始字符串。在需要对原始字符串调用方法或进行属性查找的上下文中,JavaScript 会自动包装字符串原始值并在包装器对象上调用该方法或执行属性查找。
const strPrim = "foo"; // A literal is a string primitive
const strPrim2 = String(1); // Coerced into the string primitive "1"
const strPrim3 = String(true); // Coerced into the string primitive "true"
const strObj = new String(strPrim); // String with new returns a string wrapper object.
console.log(typeof strPrim); // "string"
console.log(typeof strPrim2); // "string"
console.log(typeof strPrim3); // "string"
console.log(typeof strObj); // "object"
警告:您应该很少发现自己将 String
用作构造函数。
字符串原始值和 String
对象在使用eval()
时也会产生不同的结果。传递给 eval
的原始值被视为源代码;String
对象则像所有其他对象一样,通过返回对象来处理。例如
const s1 = "2 + 2"; // creates a string primitive
const s2 = new String("2 + 2"); // creates a String object
console.log(eval(s1)); // returns the number 4
console.log(eval(s2)); // returns the string "2 + 2"
由于这些原因,当代码遇到 String
对象(而它期望的是原始字符串)时,代码可能会中断,尽管通常情况下,作者无需担心这种区别。
可以使用valueOf()
方法将 String
对象始终转换为其原始对应物。
console.log(eval(s2.valueOf())); // returns the number 4
字符串强制转换
许多期望字符串的内置操作首先将其参数强制转换为字符串(这在很大程度上是 String
对象的行为类似于字符串原始值的原因)。该操作 可以总结如下
- 字符串按原样返回。
undefined
变为"undefined"
。null
变为"null"
。true
变为"true"
;false
变为"false"
。- 数字使用与
toString(10)
相同的算法进行转换。 - BigInts 使用与
toString(10)
相同的算法进行转换。 - Symbols 会抛出
TypeError
。 - 对象首先通过按顺序调用其转换为原始值
[Symbol.toPrimitive]()
(以"string"
作为提示)、toString()
和valueOf()
方法。然后将生成的原始值转换为字符串。
在 JavaScript 中有几种方法可以实现几乎相同的效果。
- 模板字面量:
`${x}`
恰好执行上面针对嵌入式表达式解释的字符串强制转换步骤。 String()
函数:String(x)
使用相同的算法来转换x
,只是Symbols 不会抛出TypeError
,而是返回"Symbol(description)"
,其中description
是 Symbol 的描述。- 使用
+
运算符:"" + x
会将其操作数强制转换为原始值而不是字符串,并且对于某些对象,其行为与正常的字符串强制转换完全不同。有关更多详细信息,请参阅其参考页面。
根据您的用例,您可能希望使用 `${x}`
(模仿内置行为)或 String(x)
(处理符号值而不抛出错误),但您不应使用 "" + x
。
UTF-16 字符、Unicode 代码点和音节簇
字符串从根本上表示为UTF-16 代码单元序列。在 UTF-16 编码中,每个代码单元都正好是 16 位长。这意味着最多有 216 或 65536 个字符可以用单个 UTF-16 代码单元表示。此字符集称为基本多语言平面 (BMP),其中包括最常用的字符,例如拉丁语、希腊语、西里尔字母以及许多东亚字符。每个代码单元都可以在字符串中用 \u
后跟正好四个十六进制数字来编写。
但是,整个 Unicode 字符集远大于 65536。额外的字符在 UTF-16 中以代理对的形式存储,它们是一对表示单个字符的 16 位代码单元。为避免歧义,该对的两个部分必须介于 0xD800
和 0xDFFF
之间,并且这些代码单元不用于编码单代码单元字符。(更准确地说,前导代理(也称为高代理代码单元)的值介于 0xD800
和 0xDBFF
之间(含),而后导代理(也称为低代理代码单元)的值介于 0xDC00
和 0xDFFF
之间(含)。)由一个或两个 UTF-16 代码单元组成的每个 Unicode 字符也称为Unicode 代码点。每个 Unicode 代码点都可以在字符串中用 \u{xxxxxx}
来编写,其中 xxxxxx
表示 1-6 个十六进制数字。
“孤立代理”是一个满足以下描述之一的 16 位代码单元
- 它在
0xD800
到0xDBFF
(含)范围内(即为前导代理),但它是字符串中的最后一个代码单元,或者下一个代码单元不是后导代理。 - 它在
0xDC00
到0xDFFF
(含)范围内(即为后导代理),但它是字符串中的第一个代码单元,或者上一个代码单元不是前导代理。
孤立代理不代表任何 Unicode 字符。尽管大多数 JavaScript 内置方法都能正确处理它们,因为它们都基于 UTF-16 代码单元工作,但孤立代理在与其他系统交互时通常不是有效值——例如,encodeURI()
会为孤立代理抛出URIError
,因为 URI 编码使用 UTF-8 编码,而 UTF-8 编码没有任何孤立代理的编码。不包含任何孤立代理的字符串称为格式良好的字符串,并且可以安全地与不处理 UTF-16 的函数一起使用(例如 encodeURI()
或TextEncoder
)。您可以使用isWellFormed()
方法检查字符串是否格式良好,或使用toWellFormed()
方法清理孤立代理。
除了 Unicode 字符之外,某些 Unicode 字符序列应该被视为一个视觉单元,称为音节簇。最常见的情况是表情符号:许多具有各种变化的表情符号实际上是由多个表情符号(通常由 <ZWJ>(U+200D
)字符连接)组成的。
您必须小心正在迭代的字符级别。例如,split("")
将按 UTF-16 代码单元进行拆分,并将分离代理对。字符串索引也指代每个 UTF-16 代码单元的索引。另一方面,[Symbol.iterator]()
按 Unicode 代码点进行迭代。遍历音节簇需要一些自定义代码。
"😄".split(""); // ['\ud83d', '\ude04']; splits into two lone surrogates
// "Backhand Index Pointing Right: Dark Skin Tone"
[..."👉🏿"]; // ['👉', '🏿']
// splits into the basic "Backhand Index Pointing Right" emoji and
// the "Dark skin tone" emoji
// "Family: Man, Boy"
[..."👨👦"]; // [ '👨', '', '👦' ]
// splits into the "Man" and "Boy" emoji, joined by a ZWJ
// The United Nations flag
[..."🇺🇳"]; // [ '🇺', '🇳' ]
// splits into two "region indicator" letters "U" and "N".
// All flag emojis are formed by joining two region indicator letters
构造函数
String()
-
创建
String
对象。当作为函数调用时,它会返回类型为 String 的原始值。
静态方法
String.fromCharCode()
-
使用指定的 Unicode 值序列创建并返回一个字符串。
String.fromCodePoint()
-
使用指定的代码点序列创建并返回一个字符串。
String.raw()
-
从原始模板字符串创建并返回一个字符串。
实例属性
这些属性在 String.prototype
上定义,并由所有 String
实例共享。
String.prototype.constructor
-
创建实例对象的构造函数。对于
String
实例,初始值为String
构造函数。
这些属性是每个 String
实例的自有属性。
length
-
反映字符串的
length
。只读。
实例方法
String.prototype.at()
-
返回指定
index
处的字符(正好一个 UTF-16 代码单元)。接受负整数,这些整数从最后一个字符串字符开始反向计数。 String.prototype.charAt()
-
返回指定
index
处的字符(正好一个 UTF-16 代码单元)。 String.prototype.charCodeAt()
-
返回一个数字,该数字是给定
index
处的 UTF-16 代码单元值。 String.prototype.codePointAt()
-
返回一个非负整数 Number,它是从指定
pos
开始的 UTF-16 编码代码点的代码点值。 String.prototype.concat()
-
组合两个(或多个)字符串的文本并返回一个新字符串。
String.prototype.endsWith()
-
确定字符串是否以字符串
searchString
的字符结尾。 String.prototype.includes()
-
确定调用字符串是否包含
searchString
。 String.prototype.indexOf()
-
返回
searchValue
在当前字符串中第一次出现的索引,如果未找到则返回-1
。 String.prototype.isWellFormed()
-
返回一个布尔值,指示此字符串是否包含任何孤立代理。
String.prototype.lastIndexOf()
-
返回
searchValue
在当前字符串中最后一次出现的索引,如果未找到则返回-1
。 String.prototype.localeCompare()
-
返回一个数字,指示参考字符串
compareString
在排序顺序中是在给定字符串之前、之后还是与其等效。 String.prototype.match()
-
用于将正则表达式
regexp
与字符串匹配。 String.prototype.matchAll()
-
返回
regexp
所有匹配项的迭代器。 String.prototype.normalize()
-
返回调用字符串值的 Unicode 规范化形式。
String.prototype.padEnd()
-
使用给定字符串从末尾填充当前字符串,并返回长度为
targetLength
的新字符串。 String.prototype.padStart()
-
使用给定字符串从开头填充当前字符串,并返回长度为
targetLength
的新字符串。 String.prototype.repeat()
-
返回一个由对象元素重复
count
次组成的字符串。 String.prototype.replace()
-
用于使用
replaceWith
替换searchFor
的出现次数。searchFor
可以是字符串或正则表达式,replaceWith
可以是字符串或函数。 String.prototype.replaceAll()
-
用于使用
replaceWith
替换searchFor
的所有出现次数。searchFor
可以是字符串或正则表达式,replaceWith
可以是字符串或函数。 String.prototype.search()
-
搜索正则表达式
regexp
与调用字符串之间的匹配项。 String.prototype.slice()
-
提取字符串的一部分并返回一个新字符串。
String.prototype.split()
-
返回一个字符串数组,该数组通过在调用字符串中出现子字符串
sep
的位置进行拆分来填充。 String.prototype.startsWith()
-
确定调用字符串是否以字符串
searchString
的字符开头。 String.prototype.substr()
已弃用-
返回字符串的一部分,从指定的索引开始,并向后扩展给定数量的字符。
String.prototype.substring()
-
返回一个新字符串,其中包含从(或介于)指定索引(或索引)之间的调用字符串的字符。
String.prototype.toLocaleLowerCase()
-
字符串中的字符将转换为小写,同时尊重当前区域设置。
对于大多数语言,这将返回与
toLowerCase()
相同的结果。 String.prototype.toLocaleUpperCase()
-
字符串中的字符将转换为大写,同时尊重当前区域设置。
对于大多数语言,这将返回与
toUpperCase()
相同的结果。 String.prototype.toLowerCase()
-
返回转换为小写的调用字符串值。
String.prototype.toString()
-
返回表示指定对象的字符串。覆盖了
Object.prototype.toString()
方法。 String.prototype.toUpperCase()
-
返回调用字符串值转换为大写后的结果。
String.prototype.toWellFormed()
-
返回一个字符串,其中此字符串的所有孤立代理项都替换为 Unicode 替换字符 U+FFFD。
String.prototype.trim()
-
去除字符串开头和结尾的空格。
String.prototype.trimEnd()
-
去除字符串结尾的空格。
String.prototype.trimStart()
-
去除字符串开头的空格。
String.prototype.valueOf()
-
返回指定对象的原始值。覆盖了
Object.prototype.valueOf()
方法。 String.prototype[Symbol.iterator]()
-
返回一个新的迭代器对象,该对象迭代 String 值的代码点,并将每个代码点作为 String 值返回。
HTML 包装器方法
警告: 已弃用。避免使用这些方法。
它们的用途有限,因为它们基于非常旧的 HTML 标准,并且仅提供当前可用 HTML 标签和属性的子集。其中许多在今天创建了已弃用或非标准的标记。此外,它们执行简单的字符串连接,没有任何验证或清理,这使得在使用innerHTML
直接插入时成为潜在的安全威胁。请改用DOM API,例如document.createElement()
。
String.prototype.anchor()
已弃用-
<a name="name">
(超文本目标) String.prototype.big()
已弃用<big>
String.prototype.blink()
已弃用-
<blink>
String.prototype.bold()
已弃用<b>
String.prototype.fixed()
已弃用<tt>
String.prototype.fontcolor()
已弃用String.prototype.fontsize()
已弃用String.prototype.italics()
已弃用<i>
String.prototype.link()
已弃用-
<a href="url">
(链接到 URL) String.prototype.small()
已弃用<small>
String.prototype.strike()
已弃用<strike>
String.prototype.sub()
已弃用<sub>
String.prototype.sup()
已弃用<sup>
请注意,这些方法不会检查字符串本身是否包含 HTML 标签,因此可能会创建无效的 HTML。
"</b>".bold(); // <b></b></b>
它们唯一执行的转义是将属性值中的"
(对于anchor()
、fontcolor()
、fontsize()
和link()
)替换为"
。
"foo".anchor('"Hello"'); // <a name=""Hello"">foo</a>
示例
字符串转换
String()
函数是将值转换为字符串比调用值toString()
方法更可靠的方法,因为前者在用于null
和undefined
时有效。例如
// You cannot access properties on null or undefined
const nullVar = null;
nullVar.toString(); // TypeError: Cannot read properties of null
String(nullVar); // "null"
const undefinedVar = undefined;
undefinedVar.toString(); // TypeError: Cannot read properties of undefined
String(undefinedVar); // "undefined"
规范
规范 |
---|
ECMAScript 语言规范 # sec-string-objects |
浏览器兼容性
BCD 表格仅在启用 JavaScript 的浏览器中加载。