String
[TOC]
索引
属性:
- string.length:
number
,只读,用于获取字符串中 UTF-16 编码单元的数量(非实际字符数)。 - string.constructor:
String
,指向创建该字符串对象的构造函数String。
方法:
构造函数:
- new String() / String():
(value)
,用于将任意值转换为字符串类型。它既可作为转换函数使用,也可作为构造函数创建字符串对象。
字符访问:
- string.charAt():
(index)
,用于获取指定索引位置的字符。 - string.charCodeAt():
(index?)
,用于获取指定索引位置字符的 UTF-16 编码值。 - string.at():
(index?)
,ES2022,用于获取指定索引位置的字符,支持正负索引访问。
字符串查找:
- string.indexOf():
(searchString,position?)
,区分大小写,用于查找子字符串首次出现的索引位置。从左向右进行,返回基于 0 的索引值。 - string.lastIndexOf():
(searchString,position?)
,区分大小写,用于查找子字符串最后一次出现的索引位置。搜索从字符串末尾向开头进行(从右向左),返回基于 0 的索引值。 - string.includes():
(searchString, position?)
,ES2015,区分大小写,用于判断一个字符串是否包含在另一个字符串中。 - string.startsWith():
(searchString, position?)
,ES2015,区分大小写,用于判断字符串是否以指定的子字符串开头。 - string.endsWith():
(searchString, length?)
,ES2015,区分大小写,用于判断字符串是否以指定的子字符串结尾。
字符串截取:
- string.slice():
(startIndex, endIndex?)
,用于提取字符串的指定部分并返回新字符串。支持负索引,不自动交换参数,返回新字符串。 - string.substring():
(startIndex, endIndex?)
,用于提取两个指定索引之间的字符并返回新字符串。不支持负索引,自动参数交换,返回新字符串。 - string.substr():
(startIndex, length?)
,已废弃,用于从指定位置开始提取指定长度的子字符串。支持负索引,返回新字符串。
字符串修改:
- string.toUpperCase():
()
,用于将字符串中所有字符转换为大写形式。返回新字符串,支持 Unicode 和多语言字符转换。 - string.toLowerCase():
()
,用于将字符串中所有字符转换为小写形式。返回新字符串,支持 Unicode 和多语言字符转换。 - string.concat():
(str1, str2, ..., strN)
,用于将多个字符串连接成一个新字符串。返回新字符串,功能与+
操作符相似,支持链式调用。 - string.repeat():
(count)
,ES2015,用于将字符串重复指定次数后返回新字符串。返回新字符串。 - string.trim():
()
,ES2015,用于移除字符串两端的空白字符(包括空格、制表符
\t\v
、换行符\n
等)。返回新字符串。 - string.trimStart():
()
,ES2019,用于移除字符串开头的空白字符(包括空格、制表符、换行符等)。返回新字符串,别名trimLeft()
。 - string.trimEnd():
()
,ES2019,用于移除字符串结尾的空白字符(包括空格、制表符、换行符等)。返回新字符串,别名trimRight()
。 - string.padStart():
(targetLength, padString?)
,ES2017,用于在字符串开头填充指定字符直到字符串达到目标长度。返回新字符串。 - string.padEnd():
(targetLength, padString?)
,ES2017,用于在字符串结尾填充指定字符直到字符串达到目标长度。返回新字符串。
正则表达式相关:
- string.match():
(regexp)
,支持正则,用于检索字符串中与正则表达式匹配的结果。 - string.matchAll():
(regexp)
,ES2020,支持正则,必须包含g
,用于返回包含所有匹配正则表达式的结果及分组捕获组的迭代器。 - string.search():
(regexp)
,支持正则,用于检索字符串中匹配正则表达式的第一个位置。与indexOf()
类似但支持正则表达式。 - string.replace():
(searchValue, replaceValue)
,支持正则,用于替换字符串中匹配的子串。返回新字符串,支持字符串替换和正则表达式替换,并允许使用函数进行高级处理。 - string.replaceAll():
(searchValue, replaceValue)
,ES2021,支持正则,用于全局替换字符串中所有匹配的子串。无需正则表达式即可实现全量替换。 - string.split():
(separator?, limit?)
,支持正则,用于将字符串分割为子字符串数组。支持限制返回数组长度。
其他:
- string.localeCompare():
(compareString,locales?,options?)
,用于根据当前语言环境比较两个字符串的排序顺序。提供本地化的字符串排序能力,支持多语言排序规则(如德语、中文等)。 - string.normalize():
(form?)
,ES2015,用于将字符串转换为 Unicode 标准化形式。它解决字符的多种表示方式问题(如组合字符 vs 预组合字符)。
基础
公共参数
searchString
searchString:string
,要查找的子字符串:
字符串:正常搜索。
js"abc".indexOf("b") // 1 "abcba".lastIndexOf("b") // 3 "abc".includes("b") // true "abc".startsWith("ab") // true "abc".endsWith("bc") // true
空字符串:始终返回有效索引或
true
。js"Hi".indexOf("") // 0 "Hi".lastIndexOf("") // 2 "Hi".includes("") // true "Hi".startsWith("") // true "Hi".endsWith("") // true
非字符串:强制转换为字符串。
js"123".indexOf(2) // 1 "2023".lastIndexOf(23) // 2 "123".includes(2) // true "123".startsWith(12) // true "123".endsWith(23) // true
未提供:相当于搜索 "undefined"。
js"test".indexOf() // -1(搜索"u"失败) "test".lastIndexOf() // -1 "test".includes() // false "test".startsWith() // false "test".endsWith() // false
position
position?:number
,默认:0
,开始搜索的起始位置。
正整数:从指定索引开始搜索。
js"abcabc".indexOf("a", 1) // 3 "abcabc".lastIndexOf("a", 3) // 3 "abcabc".includes("a", 1) // true "abcabc".startsWith("ca", 2) // true "abcabc".endsWith("ab", 3) // false
0:从头开始搜索。
js"abc".indexOf("a", 0) // 0 "abc".lastIndexOf("a", 0) // 0 "abc".includes("a", 0) // true "abc".startsWith("a", 0) // true "abc".endsWith("", 0) // true
负数:视为 0。
js"abc".indexOf("a", -5) // 0 "abc".lastIndexOf("c", -5) // -1 "abc".includes("a", -5) // true "abc".startsWith("a", -5) // true "abc".endsWith("a", -5) // false
浮点数:取整(非四舍五入)。
js"abc".indexOf("b", 1.9) // 1 "abc".lastIndexOf("b", 2.9) // 1 "abc".includes("b", 1.9) // true "abc".startsWith("b", 1.9) // true "abc".endsWith("c", 2.9) // false
超出长度:直接返回 -1。
js"abc".indexOf("a", 10) // -1 "abc".lastIndexOf("a", 10) // 0 "abc".includes("a", 10) // false "abc".startsWith("a", 10) // false "abc".endsWith("c", 10) // true
搜索方法对比
方法 | 区分大小写 | 搜索方向 | 返回值 | 空串处理 | 典型用例 | 性能 |
---|---|---|---|---|---|---|
indexOf() | ✅ | 正向 | 首次索引/-1 | 返回位置 | 需要位置信息 | O(n) |
lastIndexOf() | ✅ | 反向 | 最后索引/-1 | 返回位置 | 需要位置信息 | O(n) |
includes() | ✅ | 正向 | 布尔值 | 始终true | 存在性检查 | O(n) |
search() | ✅ | 正向 | 索引/-1(支持正则) | 返回0 | 正则搜索 | 可变 |
match() | ✅ | 正向 | 匹配数组 | 返回匹配 | 正则匹配 | 【】 |
startsWith() | ✅ | 正向 | 布尔值 | 始终true | 检查开头 | O(1)* |
endsWith() | ✅ | 正向 | 布尔值 | 始终true | 检查结尾 | O(1)* |
注意:startsWith()
/endsWith()
在指定位置时性能可能优于 includes()
。
String
属性
length
string.length:number
,只读,用于获取字符串中 UTF-16 编码单元的数量(非实际字符数)。
返回:
count:
number
,非负整数(0 或正整数)。
语法特性:
编码单元计数:按 UTF-16 编码计数(代理对占 2 个长度)。
js"Hello".length; // 5 "中文".length; // 2 "🚀".length; // 2(火箭表情为代理对)
不同字符类型的长度计算
字符类型 示例 实际字符数 .length
说明 ASCII 字符 "A"
1 1 单编码单元 基本多文种平面 "中文"
2 2 单编码单元/字符 代理对(表情) "🚀"
1 2 双编码单元 组合字符 "é"
1 2 基础字符 + 重音组合标记 零宽连接符 "👨👩👧"
1(家庭) 8 多编码单元组合 空字符串:
""
的长度为 0。自动装箱:原始字符串访问时创建临时String对象。
Unicode 敏感:特殊字符可能返回大于 1 的长度。
常见问题:
代理对字符计数错误:
js"🚀".length; // 2(但实际是 1 个字符)
解决方案:
js// 方法 1:使用展开运算符 [..."🚀"].length; // 1 // 方法 2:使用 Intl.Segmenter (ES2022) new Intl.Segmenter().segment("🚀").length; // 1
组合字符被拆分:
js"café".length; // 4(但 é 是 e + ́ 组合)
解决方案:标准化后计算长度。
js"café".normalize().length; // 4(标准化后仍为 4) // 正确统计需要字形簇计数
不可见字符影响长度:
js"Hi\u200b".length; // 3(含零宽空格)
解决方案:替换不可见字符为
""
js"Hi\u200b".replace(/\u200b/g, "").length; // 2
示例:
验证输入非空:
jsfunction validateInput(text) { return text.length > 0; } validateInput(""); // false
循环遍历字符:
jsconst str = "A🚀"; for (let i = 0; i < str.length; i++) { console.log(str[i]); } // 输出: "A", "\uD83D", "\uDE80"(拆分了代理对)
获取最后一个字符:
jsfunction lastChar(str) { return str.charAt(str.length - 1); } lastChar("React"); // "t"
截断长文本:
jsfunction truncate(text, maxLength) { return text.length > maxLength ? text.slice(0, maxLength) + "..." : text; } truncate("Long text here", 8); // "Long tex..."
constructor
string.constructor:String
,指向创建该字符串对象的构造函数String。
返回:
str:
String
,创建该字符串的构造函数引用(默认为String
函数)。
语法:
// 字符串原始值(自动装箱)
"text".constructor === String; // true
// 显式创建的字符串对象
const strObj = new String("text");
strObj.constructor === String; // true
核心特性:
原型继承:继承自
Object.prototype.constructor
。可修改 constructor:可被重新赋值(强烈不建议)。
js// 自定义字符串类型 function MyString(value) { this.value = value; } // 继承 String 原型的方法 MyString.prototype = Object.create(String.prototype); // 重写 constructor 指向 MyString.prototype.constructor = MyString; const myStr = new MyString("Custom"); console.log(myStr.constructor.name); // "MyString" console.log(myStr.toUpperCase()); // "CUSTOM"(继承方法)
类型安全检测:用于判断对象是否为字符串类型。
jsfunction isString(value) { return value?.constructor === String; } isString("hello"); // true isString(new String("")); // true isString(123); // false
原始值自动装箱:访问原始字符串的
.constructor
会创建临时对象。js// 临时对象创建演示 let count = 0; const proxy = new Proxy(String, { construct() { count++; return Reflect.construct(...arguments); } }); String = proxy; "test".constructor; // 触发装箱,count 变为 1
优化处理:
安全替代方案:优先使用类型安全的检测方式。
- 优先使用
typeof
检测原始字符串。 - 使用
instanceof
检测字符串对象。
js// 更可靠的字符串检测 function isSafeString(value) { return typeof value === 'string' || (value instanceof String); }
- 优先使用
示例:
构造函数访问:
jsconst str = "prototype"; const constructor = str.constructor; // String // 通过构造函数创建新实例 const newStr = new constructor("New string"); console.log(newStr); // String {"New string"}
跨上下文类型检查:
js// iframe 中的字符串 const iframe = document.createElement('iframe'); document.body.appendChild(iframe); const iframeStr = iframe.contentWindow.String; // 检测跨上下文字符串 const crossStr = new iframeStr("test"); console.log(crossStr.constructor === String); // false console.log(crossStr.constructor.name === "String"); // true
方法
构造函数
new String()
new String() / String():(value)
,用于将任意值转换为字符串类型。它既可作为转换函数使用,也可作为构造函数创建字符串对象。
value:
any
,接受任何 JS数据类型作为参数。返回:
str:
string|String
,string
:作为函数调用时,返回原始字符串值,不可变。String
:作为构造函数时,返回 String 对象,包含特殊属性和方法。
语法:
// 作为转换函数(推荐)
String(value)
// 作为构造函数(不推荐)
new String(value)
核心特性:
转换规则:遵循 JS 的隐式转换规则:
原始类型转换:
原始类型 转换结果 示例 Undefined
"undefined"
String(undefined)
Null
"null"
String(null)
Boolean
"true"
或"false"
String(true) → "true"
Number
数字的字符串表示 String(3.14) → "3.14"
BigInt
去掉末尾的 n
String(10n) → "10"
String
原值(无变化) String("hi") → "hi"
Symbol
"Symbol(描述)"
String(Sym()) → "Symbol()"
对象类型转换:
优先调用
toString()
方法jsString({ toString: () => "custom" }) // "custom"
无
toString()
时调用valueOf()
jsString({ valueOf: () => 42 }) // "42"
默认对象转换
jsString({}) // "[object Object]" String([]) // "" (空数组) String([1, 2]) // "1,2"
特殊值处理:
值 转换结果 NaN
"NaN"
Infinity
"Infinity"
-0
"0"
空数组 []
""
(空字符串)函数 函数源码字符串 Date
对象日期字符串 RegExp
对象正则表达式字符串
对比 toString():
特性 String(value)
value.toString()
处理 null ✅ 返回 "null"
❌ 抛出 TypeError 处理 undefined ✅ 返回 "undefined"
❌ 抛出 TypeError 安全性 ✅ 总是安全 ❌ 原始值需包装 数字转换 ✅ 直接转换 ❌ 需要指定基数 使用场景 通用类型转换 对象自定义字符串表示 js// 安全转换 String(null) // "null" null.toString() // TypeError! // 数字转换 String(15) // "15" (15).toString() // "15" (需括号) (15).toString(2) // "1111" (二进制)
示例:
自定义对象转换:
jsclass Product { constructor(name, price) { this.name = name; this.price = price; } toString() { return `${this.name} - ¥${this.price}`; } } const p = new Product("耳机", 299); String(p); // "耳机 - ¥299"
字符访问
charAt()
string.charAt():(index)
,用于获取指定索引位置的字符。
index:
number
,要获取字符的索引位置。参数类型 处理方式 示例 整数 直接作为索引 "A".charAt(0)
→"A"
浮点数 自动取整(非四舍五入) "AB".charAt(1.9)
→"B"
负数 返回空字符串 "Hi".charAt(-1)
→""
超出范围 返回空字符串 "OK".charAt(2)
→""
非数值 转换为 0 "X".charAt("a")
→"X"
未提供 默认使用 0 "Y".charAt()
→"Y"
返回:
str:
string
,指定位置的字符或空字符串(当索引无效时)。返回的是 UTF-16 编码单元,而非完整字符。
语法特性:
特殊字符处理:
代理对字符(如表情符号)
jsconst rocket = "🚀"; rocket.length; // 2 rocket.charAt(0); // "\uD83D"(高代理项) rocket.charAt(1); // "\uDE80"(低代理项)
组合字符
jsconst cafe = "café"; // "e" + 重音符组合 cafe.charAt(3); // "e" cafe.charAt(4); // "\u0301"(重音符)
最佳实践:
方法推荐:
- 简单 ASCII 文本优先使用
charAt()
。 - 多语言/表情符号文本使用
at()
或Array.from()
。 - 需要字符编码时使用
charCodeAt()
/codePointAt()
。
- 简单 ASCII 文本优先使用
索引验证:始终检查索引是否在有效范围内。
jsif (index >= 0 && index < str.length) { const char = str.charAt(index); }
处理复杂字符:对于代理对或组合字符,使用更现代的方法
js// 获取完整字符的方式 Array.from("🚀")[0]; // "🚀" "🚀".at(0); // "🚀" (ES2022)
性能考虑:在循环中访问字符时,缓存字符串长度
jsconst len = str.length; for (let i = 0; i < len; i++) { str.charAt(i); }
空值处理:确保输入是字符串类型
jsfunction safeCharAccess(input, index) { if (typeof input !== "string") return ""; return input.charAt(index); }
示例:
获取首字母:
jsfunction getFirstChar(str) { return str.charAt(0); } getFirstChar("React"); // "R"
遍历字符串(不推荐用于复杂字符):
jsconst str = "ABC"; for (let i = 0; i < str.length; i++) { console.log(str.charAt(i)); } // 输出: "A", "B", "C"
实现字符串反转
jsfunction reverseString(str) { let result = ""; for (let i = str.length - 1; i >= 0; i--) { result += str.charAt(i); } return result; } reverseString("123"); // "321"
charCodeAt()
string.charCodeAt():(index?)
,用于获取指定索引位置字符的 UTF-16 编码值。
index?:
number
,默认:0
,要获取编码的字符位置。参数类型 处理方式 示例 整数 直接作为索引 "A".charCodeAt(0)
→65
浮点数 自动取整(向下取整) "B".charCodeAt(0.9)
→66
(B 的 ASCII)负数 返回 NaN
"C".charCodeAt(-1)
→NaN
超出范围 返回 NaN
"OK".charCodeAt(3)
→NaN
非数值 转换为 0 "X".charCodeAt("a")
→88
(X 的 ASCII)未提供 默认使用 0 "Y".charCodeAt()
→89
返回:
- code:
number
,范围:0~65535整数
,指定位置的 UTF-16 编码值,无效索引返回NaN
。
- code:
语法特性:
Unicode 处理机制:
基本多文种平面 (BMP)
js// U+0041 拉丁字母 A "A".charCodeAt(0); // 65 (十六进制 0x0041) // U+4E2D 中文"中" "中".charCodeAt(0); // 20013 (十六进制 0x4E2D)
辅助平面字符 (代理对)
js// U+1F680 火箭表情 🚀 const rocket = "🚀"; rocket.length; // 2 (代理对) rocket.charCodeAt(0); // 55357 (0xD83D, 高代理) rocket.charCodeAt(1); // 56960 (0xDE80, 低代理)
组合字符
js// é = e(U+0065) + 重音符(U+0301) "é".charCodeAt(0); // 101 (e) "é".charCodeAt(1); // 769 (重音符)
最佳实践:
空字符串处理:
js"".charCodeAt(0); // NaN "".charCodeAt(); // NaN
无效索引防御:
jsfunction safeCharCode(str, index) { if (index < 0 || index >= str.length) return -1; return str.charCodeAt(index); }
获取完整代理对码点
js// 获取完整码点 function getFullCodePoint(str, index) { const high = str.charCodeAt(index); if (high >= 0xD800 && high <= 0xDBFF && index + 1 < str.length) { const low = str.charCodeAt(index + 1); return (high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000; } return high; } getFullCodePoint("🚀", 0); // 128640 (0x1F680)
组合字符警告:重音字符返回多个编码值
js// 重音字符返回多个编码值 "café".charCodeAt(3); // 101 (e) "café".charCodeAt(4); // 769 (重音符)
示例:
字符编码转换:
jsfunction toHexString(str) { let hex = ""; for (let i = 0; i < str.length; i++) { const code = str.charCodeAt(i); hex += "U+" + code.toString(16).toUpperCase().padStart(4, '0') + " "; } return hex.trim(); } toHexString("A中"); // "U+0041 U+4E2D"
ASCII 值检测:
jsfunction isASCII(str) { for (let i = 0; i < str.length; i++) { if (str.charCodeAt(i) > 127) return false; } return true; } isASCII("Hello"); // true isASCII("Héllo"); // false
自定义字符串加密:
jsfunction simpleEncrypt(text, key) { let result = ""; for (let i = 0; i < text.length; i++) { const code = text.charCodeAt(i); result += String.fromCharCode(code ^ key); // ^: 按位异或() } return result; } const encrypted = simpleEncrypt("Secret", 42);
代理对检测
jsfunction isSurrogatePairAt(str, index) { const code = str.charCodeAt(index); return code >= 0xD800 && code <= 0xDBFF; // 高代理区域 } isSurrogatePairAt("🚀", 0); // true
at()
string.at():(index?)
,ES2022,用于获取指定索引位置的字符,支持正负索引访问。
index?:
number
,字符位置索引,支持负数(从末尾计数)。参数类型 处理方式 示例 正整数 从开头计数 (0-based) "ABC".at(1)
→"B"
负整数 从末尾计数 (-1 为最后字符) "XYZ".at(-1)
→"Z"
浮点数 自动取整(向零取整) "123".at(1.9)
→"2"
超出范围 返回 undefined
"OK".at(3)
→undefined
非数值 转换为 0 "X".at("a")
→"X"
未提供 默认使用 0 "Y".at()
→"Y"
返回:
str:
string|undefined
,指定位置的字符或undefined
(索引无效时)。
语法特性:
Unicode 处理特性:
代理对字符完整返回
js"🚀".at(0); // "🚀"(完整字符) "👨👩👧".at(0); // "👨"(首个字符,注意零宽连接符)
组合字符独立返回
js"café".at(3); // "e" "café".at(4); // "\u0301"(组合重音符)
多码点字符处理
jsconst flag = "🇨🇳"; // U+1F1E8 和 U+1F1F3 flag.at(0); // "🇨"(U+1F1E8) flag.at(1); // "🇳"(U+1F1F3)
浏览器兼容性与 polyfill:
兼容性支持
- 现代浏览器:Chrome 92+, Firefox 90+, Safari 15.4+
- Node.js:16.6.0+
- 不支持环境:IE, 旧版移动浏览器
Polyfill 实现
jsif (!String.prototype.at) { String.prototype.at = function(index) { // 处理非数值和未定义 index = Math.trunc(index) || 0; // 处理负索引 if (index < 0) index += this.length; // 边界检查 if (index < 0 || index >= this.length) return undefined; // 返回字符(保持代理对完整) return String.fromCodePoint(this.codePointAt(index)); }; }
性能优化:
避免在循环中重复计算长度:
jsconst len = str.length; for (let i = -len; i < len; i++) { str.at(i); }
优先用于负索引场景:
js// 优于 str.charAt(str.length - 1); // 使用 str.at(-1);
复杂字符处理:
js// 需要完整表情符号时 Array.from(str)[0]; // 替代方案
输入验证
jsfunction safeAt(str, index) { if (typeof str !== "string") return undefined; return str.at(index); }
示例:
基本使用:
js// 基础示例 "JavaScript".at(0); // "J" "前端".at(-1); // "端"(负索引支持) "🚀".at(0); // "🚀"(完整字符)
循环遍历(支持反向):
jsfunction reverseTraversal(str) { for (let i = -1; i >= -str.length; i--) { console.log(str.at(i)); } } reverseTraversal("123"); // 输出 "3", "2", "1"
字符串查找
indexOf()
string.indexOf():(searchString,position?)
,区分大小写,用于查找子字符串首次出现的索引位置。从左向右进行,返回基于 0 的索引值。
searchString:
string
,要查找的子字符串。position?:
number
,默认:0
,开始搜索的起始位置。返回:
index:
number
,首次出现的索引,未找到返回-1
。
语法特性:
特殊搜索行为:
空字符串处理
js"Hello".indexOf(""); // 0(位置0存在空串) "".indexOf(""); // 0 "ABC".indexOf("", 2); // 2(从位置2开始空串存在)
Unicode 字符搜索
js"café".indexOf("é"); // 3 "🚀火箭".indexOf("火"); // 2(🚀占2个编码单元)
大小写敏感
js"JavaScript".indexOf("script"); // -1(区分大小写)
最佳实践:
统计子串出现次数:缓存长度提升循环性能。
jsfunction countOccurrences(str, substring) { let count = 0; let pos = 0; const subLen = substring.length; while ((pos = str.indexOf(substring, pos)) !== -1) { count++; pos += subLen; // // 跳过已匹配部分 } return count; } countOccurrences("abababa", "aba"); // 2
优先使用
includes()
检查存在性:js// 更语义化的写法 if (str.includes(substring)) { ... }
处理非字符串输入:使用String()转换成字符串。
jsfunction safeIndexOf(input, search, position = 0) { if (typeof input !== "string") input = String(input); return input.indexOf(search, position); }
大文本搜索优化:使用
Boyer-Moore
算法替代(第三方库)js// 使用 Boyer-Moore 算法替代(第三方库) import { boyerMoore } from 'search-algorithms'; boyerMoore(largeText, pattern);
组合字符警告:先标准化字符串,再查找
js"café".indexOf("é"); // 可能返回 -1(取决于组合方式) // 解决方案:标准化字符串 "café".normalize().indexOf("é"); // 3
示例:
检查子串存在性:
jsfunction contains(str, substring) { return str.indexOf(substring) !== -1; } contains("Hello World", "World"); // true
提取子串后的内容:
jsfunction afterFirst(str, delimiter) { const index = str.indexOf(delimiter); return index === -1 ? str : str.slice(index + delimiter.length); } afterFirst("2023-06-16", "-"); // "06-16"
条件分支处理
jsconst userAgent = navigator.userAgent; let browser = "Unknown"; if (userAgent.indexOf("Chrome") !== -1) { browser = "Chrome"; } else if (userAgent.indexOf("Firefox") !== -1) { browser = "Firefox"; }
lastIndexOf()
string.lastIndexOf():(searchString,position?)
,区分大小写,用于查找子字符串最后一次出现的索引位置。搜索从字符串末尾向开头进行(从右向左),返回基于 0 的索引值。
searchString:
string
,要查找的子字符串。返回:
index:
number
,最后一次出现的索引,未找到返回-1
。
语法特性:
搜索方向:从右向左找。
js// 从位置4向开头搜索 "ababab".lastIndexOf("ab", 4); // 4(从右向左找)
空字符串处理:
js"Hello".lastIndexOf(""); // 5(字符串末尾) "".lastIndexOf(""); // 0 "ABC".lastIndexOf("", 1); // 1(位置1存在空串)
Unicode 支持:
js"café café".lastIndexOf("é"); // 8(第二个é) "🚀🚀火箭".lastIndexOf("🚀"); // 2(第二个🚀的起始位置)
最佳实践:类似indexOf()。
示例:
获取文件扩展名:
jsfunction getFileExtension(filename) { const dotIndex = filename.lastIndexOf("."); return dotIndex === -1 ? "" : filename.slice(dotIndex + 1); } getFileExtension("archive.tar.gz"); // "gz"
提取域名:
jsfunction getDomain(url) { const protocolEnd = url.indexOf("://"); const domainStart = protocolEnd !== -1 ? protocolEnd + 3 : 0; const domainEnd = url.indexOf("/", domainStart); return url.slice(domainStart, domainEnd !== -1 ? domainEnd : undefined); } getDomain("https://example.com/path"); // "example.com"
替换最后一次出现的子串:
jsfunction replaceLast(str, search, replace) { const index = str.lastIndexOf(search); return index === -1 ? str : str.slice(0, index) + replace + str.slice(index + search.length); } replaceLast("a-b-c-d", "-", "."); // "a-b.c-d"
解析查询参数值
jsfunction getQueryParam(url, param) { const paramStart = url.lastIndexOf(param + "="); if (paramStart === -1) return null; const valueStart = paramStart + param.length + 1; const valueEnd = url.indexOf("&", valueStart); return valueEnd === -1 ? url.slice(valueStart) : url.slice(valueStart, valueEnd); } getQueryParam("page.com?name=John&age=30", "age"); // "30"
验证文件类型
jsconst ALLOWED_EXT = [".jpg", ".png"]; function isValidImage(filename) { const lowerName = filename.toLowerCase(); return ALLOWED_EXT.some(ext => lowerName.lastIndexOf(ext) === filename.length - ext.length ); } isValidImage("photo.PNG"); // true
includes()
string.includes():(searchString, position?)
,ES2015,区分大小写,用于判断一个字符串是否包含在另一个字符串中。
searchString:
string
,要搜索的子字符串。position?:
number
,默认:0
,开始搜索的起始位置。返回:
isIncludes:
booleans
,包含返回true
,否则返回false
。
语法特性:
空字符串行为:
js"Hello".includes(""); // true(空字符串永远存在) "".includes(""); // true
Unicode 支持:
js"café".includes("é"); // true "🚀火箭".includes("火"); // true
大小写敏感:
js"JavaScript".includes("script"); // false(区分大小写)
组合字符处理:需事先标准化。
jsconst combined = "e\u0301"; // é combined.includes("é"); // false(标准化后为 true) combined.normalize().includes("é"); // true
性能优化:
大小写不敏感优化:一次性转换避免重复计算。
js// 一次性转换避免重复计算 function caseInsensitiveIncludes(str, search) { const lowerStr = str.toLowerCase(); return lowerStr.includes(search.toLowerCase()); }
大文本搜索优化:限制搜索范围。
js// 限制搜索范围 const MAX_SEARCH_LENGTH = 1000; function safeIncludes(largeText, substring) { return largeText.includes( substring, Math.max(0, largeText.length - MAX_SEARCH_LENGTH) ); }
空值安全处理:
jsfunction safeIncludes(input, search) { if (typeof input !== "string") return false; return input.includes(search); } safeIncludes(null, "error"); // false
组合字符处理
jsfunction unicodeIncludes(str, search) { return str.normalize().includes(search.normalize()); } unicodeIncludes("cafe\u0301", "é"); // true
示例:
简单存在性检查:
jsfunction hasKeyword(text, keyword) { return text.includes(keyword); } hasKeyword("Error: File not found", "Error"); // true
权限验证:
jsfunction checkPermission(userRoles) { return userRoles.includes("admin") || userRoles.includes("editor"); } checkPermission(["user", "editor"]); // true
搜索过滤:
jsconst products = ["iPhone", "iPad", "MacBook"]; const filtered = products.filter(product => product.toLowerCase().includes("ip") ); // ["iPhone", "iPad"]
文件类型检查
jsfunction isImageFile(filename) { const lower = filename.toLowerCase(); return [".png", ".jpg", ".gif"].some(ext => lower.includes(ext)); } isImageFile("photo.PNG"); // true
带偏移量的检查
jsfunction isSecondOccurrence(text, word) { const firstIndex = text.indexOf(word); return firstIndex !== -1 && text.includes(word, firstIndex + 1); } isSecondOccurrence("a-b-a-c", "a"); // true
startsWith()
string.startsWith():(searchString, position?)
,ES2015,区分大小写,用于判断字符串是否以指定的子字符串开头。
searchString:
string
,要检查的前缀字符串。position?:
number
,默认:0
,开始搜索的起始位置。返回:
isStartsWith:
boolean
,以该前缀开头返回true
,否则返回false
。
核心特性:
空字符串行为:空字符串永远匹配开头
js"Hello".startsWith(""); // true(空字符串永远匹配开头) "".startsWith(""); // true
大小写敏感:
js"JavaScript".startsWith("java"); // false(区分大小写)
示例:
基本使用:
js"https://bank.com".startsWith("https://") // true
带偏移量的检查:
js"Hello World".startsWith("World", 6) // true
进阶扩展:
大小写不敏感优化:统一转换为小写比较。
js// 统一转换为小写比较 function caseInsensitiveStartsWith(str, prefix) { return str.toLowerCase().startsWith(prefix.toLowerCase()); } caseInsensitiveStartsWith("JavaScript", "JAVA"); // true
长前缀快速失败:先检查长度避免不必要的比较。
js// 先检查长度避免不必要的比较 function safeStartsWith(str, prefix) { if (prefix.length > str.length) return false; return str.startsWith(prefix); } safeStartsWith("short", "very_long_prefix"); // false
空值安全处理
jsfunction safeStartsWith(input, prefix) { if (typeof input !== "string") return false; return input.startsWith(prefix); } safeStartsWith(null, "http"); // false
性能关键场景:避免在循环中重复转换大小写。
js// 避免在循环中重复转换大小写 const lowerStr = str.toLowerCase(); for (const prefix of prefixes) { lowerStr.startsWith(prefix.toLowerCase()); }
组合字符处理:事先使用 normalize() 标准化处理。
jsfunction unicodeStartsWith(str, prefix) { return str.normalize().startsWith(prefix.normalize()); } unicodeStartsWith("e\u0301cole", "école"); // true
endsWith()
string.endsWith():(searchString, length?)
,ES2015,区分大小写,用于判断字符串是否以指定的子字符串结尾。
searchString:
string
,要检查的后缀字符串。length?:
number
,默认:string.length
,指定字符串的有效长度。正整数
:在指定长度内检查结尾。"abcabc".endsWith("ab", 3)
→false
0
:检查空字符串。"abc".endsWith("", 0)
→true
负数
:视为 0。"abc".endsWith("a", -5)
→false
浮点数
:取整(非四舍五入)。"abc".endsWith("c", 2.9)
→false
超出长度
:视为字符串长度。"abc".endsWith("c", 10)
→true
返回:
isEndsWith:
boolean
,以该后缀结尾返回true
,否则返回false
。
核心特性:
空字符串行为:
js"Hello".endsWith(""); // true(空字符串永远匹配结尾) "".endsWith(""); // true
大小写敏感:
js"JavaScript".endsWith("script"); // false(区分大小写)
Unicode 支持:
代理对:支持
js"火箭🚀".endsWith("🚀"); // true
组合字符处理:需标准化再使用
js"café".endsWith("fé"); // false(é 是 e+重音符) "café".normalize().endsWith("é"); // true
示例:
基本使用:
js"JavaScript".endsWith("Script"); // true "document.pdf".endsWith(".pdf"); // true "前端开发".endsWith("开发"); // true
带长度限制的检查:
jsfunction isLastName(str, name, maxLength) { return str.endsWith(name, maxLength); } // 在10个字符内检查是否以"Smith"结尾 isLastName("John Smith", "Smith", 10); // true
进阶扩展:类似 startsWith()
字符串截取
slice()
string.slice():(startIndex, endIndex?)
,用于提取字符串的指定部分并返回新字符串。支持负索引,不自动交换参数,返回新字符串。
startIndex:
number
,提取起始位置(包含)。假设str = "ABCDE"
:正整数
:从索引处开始。str.slice(1)
→"BCDE"
负整数
:从末尾计算(-1=最后字符)。str.slice(-3)
→"CDE"
0
:从头开始。str.slice(0)
→"ABCDE"
浮点数
:向零取整。str.slice(1.7)
→"BCDE"
超出长度
:返回空字符串。str.slice(10)
→""
NaN
:视为 0。str.slice(NaN)
→"ABCDE"
endIndex?:
number
,提取结束位置(不包含)。省略时提取到字符串末尾。假设str = "ABCDE"
:正整数
:提取到此索引前(不包含)。str.slice(1, 3)
→"BC"
负整数
:从末尾计算。str.slice(1, -1)
→"BCD"
0
:返回空字符串(因 end≤start)。str.slice(2, 0)
→""
未提供
:提取到字符串末尾。str.slice(2)
→"CDE"
大于长度
:视为字符串长度。str.slice(0, 10)
→"ABCDE"
返回:
slicedStr:
string
,返回新字符串,包含提取的子字符串。
核心特性:
负索引转换规则:转换为
len + index
jsconst str = "ABCDE"; str.slice(-3); // 等价于 str.slice(2) → "CDE" str.slice(1, -2); // 等价于 str.slice(1, 3) → "BC"
不修改原字符串:
jsconst original = "Hello"; const sliced = original.slice(1); console.log(original); // "Hello"(未改变) console.log(sliced); // "ello"
Unicode 处理:
代理对字符(如表情符号):占据2个字符
js"🚀火箭".slice(0, 1); // "�"(无效字符) "🚀火箭".slice(0, 2); // "🚀" "🚀火箭".slice(2, 4); // "火箭"
组合字符:
\u0301
算一个字符jsconst cafe = "cafe\u0301"; // café cafe.slice(0, 4); // "cafe"(不包含重音符) cafe.slice(0, 5); // "café"
示例:
提取文件名(无扩展名):
jsfunction getBaseName(filename) { const dotIndex = filename.lastIndexOf("."); return dotIndex === -1 ? filename : filename.slice(0, dotIndex); } getBaseName("archive.tar.gz"); // "archive.tar"
手机号脱敏处理:
jsfunction maskPhone(phone) { return phone.slice(0, 3) + "****" + phone.slice(-4); } maskPhone("13800138000"); // "138****8000"
进阶扩展:
负索引性能低:直接使用正索引更快(避免负索引转换)
js// 直接使用正索引更快(避免负索引转换) const str = "Data"; str.slice(str.length - 3); // 优于 str.slice(-3)
大字符串优化:使用位置指针循环切片
js// 避免重复切片大字符串 const largeText = "VeryLongText..."; // 错误:多次切片创建新字符串 const part1 = largeText.slice(0, 1000); const part2 = largeText.slice(1000, 2000); // 正确:使用位置指针 let pos = 0; while (pos < largeText.length) { const chunk = largeText.slice(pos, pos + 1000); pos += 1000; }
代理对安全处理:转换为数组处理代理对
jsfunction safeSlice(str, start, end) { // 转换为数组处理代理对 return Array.from(str).slice(start, end).join(""); } safeSlice("🚀火箭", 0, 1); // "🚀"
组合字符处理:标准化后再切片
js// 标准化后再切片 "cafe\u0301".normalize().slice(0, 4); // "café"
避免链式调用
js// 错误:多次创建临时字符串 str.slice(1).slice(0, 3); // 正确:单次切片 str.slice(1, 4);
substring()
string.substring():(startIndex, endIndex?)
,用于提取两个指定索引之间的字符并返回新字符串。不支持负索引,自动参数交换,返回新字符串。
startIndex:
number
,提取起始位置(包含)。endIndex?:
number
,提取结束位置(不包含)。省略时提取到字符串末尾。参数组合 ( str = "ABCDE"
)等效调用 结果 说明 substring(2)
substring(2, 5)
"CDE"
省略 endIndex 时提取到末尾 substring(2, 0)
substring(0, 2)
"AB"
自动交换参数 substring(-3, 2)
substring(0, 2)
"AB"
负值视为 0 substring(3, 10)
substring(3, 5)
"DE"
超出长度视为字符串长度 substring(NaN, 2)
substring(0, 2)
"AB"
NaN 视为 0 substring(2, undefined)
substring(2)
"CDE"
undefined 视为未提供 返回:
substring:
string
,新字符串,包含提取的子字符串。
核心特性:
对比 slice():
不同:负值处理:不支持负索引,负值视为 0。
js"ABCDE".substring(-5, 2); // 转为 substring(0, 2) → "AB" "ABCDE".substring(2, -1); // 转为 substring(0, 2) → "AB"
不同:自动参数交换。
js"ABCDE".substring(3, 1); // 自动转为 substring(1, 3) → "BC"
相同:不修改原字符串
jsconst str = "Hello"; str.substring(1, 3); // "el" console.log(str); // "Hello"(不变)
相同:Unicode 处理
js// 代理对字符 "🚀火箭".substring(0, 2); // "🚀"(正确提取代理对) "🚀火箭".substring(2, 4); // "火箭" // 组合字符 const cafe = "cafe\u0301"; // café cafe.substring(0, 4); // "cafe"(不包含重音符) cafe.substring(0, 5); // "café"(包含重音符)
进阶扩展:
避免负参数:负参数触发转换逻辑(性能略低)
js// 负参数触发转换逻辑(性能略低) str.substring(1, -1); // 转为 str.substring(0, 1) // 改用正索引 str.substring(0, 1);
大字符串优化:避免重复提取相同位置
js// 避免重复提取相同位置 const largeText = "VeryLongText..."; // 错误:两次提取相同范围 const part1 = largeText.substring(0, 1000); const part2 = largeText.substring(0, 1000);
组合字符处理:标准化后提取
js// 标准化后提取 "cafe\u0301".normalize().substring(0, 4); // "café"
等值参数短路优化
jsfunction safeSubstring(str, start, end) { if (start === end) return ""; return str.substring(start, end); } safeSubstring("ABC", 1, 1); // ""
替代方案选择:
- 需要负索引时使用 slice
- 需要位置交换保障时用 substring
js// 需要负索引时使用 slice "End".slice(-3); // "End" // 需要位置交换保障时用 substring const start = 5, end = 2; "Data".substring(start, end); // "Da"(自动交换)
substr()
string.substr():(startIndex, length?)
,已废弃,用于从指定位置开始提取指定长度的子字符串。支持负索引,返回新字符串。
startIndex:
number
,提取起始位置(支持负索引)。length?:
number
,提取的字符数。省略时提取到字符串末尾。返回:
substr:
string
,新字符串,包含提取的子字符串。
核心特性:
- 支持负索引
- 返回新字符串
- 废弃原因:
- 非 ECMAScript 标准(浏览器实现存在差异)
- 参数语义容易混淆(位置+长度 vs 起止位置)
- Unicode 支持不完善
- 对比 slice():第二个参数表示提取字符长度,而非结束索引。
示例:
基本使用:
jsconst aString = "Mozilla"; console.log(aString.substr(0, 1)); // 'M' console.log(aString.substr(1, 0)); // '' console.log(aString.substr(-1, 1)); // 'a' console.log(aString.substr(1, -1)); // '' console.log(aString.substr(-3)); // 'lla' console.log(aString.substr(1)); // 'ozilla' console.log(aString.substr(-20, 2)); // 'Mo' console.log(aString.substr(20, 2)); // ''
字符串修改
toUpperCase()
string.toUpperCase():()
,用于将字符串中所有字符转换为大写形式。返回新字符串,支持 Unicode 和多语言字符转换。
返回:
upperCase:
string
, 新字符串,全部字符转换为大写后的结果。
核心特性:
不修改原字符串
非字母字符保留原样:
js"123!@#".toUpperCase(); // "123!@#" "こんにちは".toUpperCase(); // "こんにちは"(日语假名不变)
Unicode 支持:
字符类型 转换示例 拉丁字母 "a" → "A"
带重音字符 "é" → "É"
希腊字母 "α" → "Α"
西里尔字母 "я" → "Я"
特殊符号 "ß" → "SS"
(德语特殊规则)
示例:
基本使用:
js// 基础示例 "hello".toUpperCase(); // "HELLO" "café".toUpperCase(); // "CAFÉ" "αβγ".toUpperCase(); // "ΑΒΓ"(希腊字母)
toLowerCase()
string.toLowerCase():()
,用于将字符串中所有字符转换为小写形式。返回新字符串,支持 Unicode 和多语言字符转换。
返回:
lowerCase:
string
,新字符串,全部字符转换为小写后的结果。
核心特性:类似 toUpperCase()
不修改原字符串
非字母字符保留原样
Unicode 支持
示例:
基本使用:
js// 基础示例 "HELLO".toLowerCase(); // "hello" "CAFÉ".toLowerCase(); // "café" "ΑΒΓ".toLowerCase(); // "αβγ"(希腊字母)
concat()
string.concat():(str1, str2, ..., strN)
,用于将多个字符串连接成一个新字符串。返回新字符串,功能与 +
操作符相似,支持链式调用。
str1, str2, ..., strN:
string
,要连接的字符串(至少一个,可多个)。示例("A".concat(...)
):字符串
:直接连接。"B"
→"AB"
非字符串
:转换为字符串。123
→"A123"
空值
:转换为 "null"/"undefined"。null
→"Anull"
数组
:元素转换为字符串后连接。["B","C"]
→"AB,C"
对象
:调用toString()
。{}
→"A[object Object]"
未提供
:返回原字符串副本。( )
→"A"
返回:
newStr:
string
,新字符串,原始字符串与所有参数连接后的结果。
核心特性:
不修改原字符串
自动类型转换:
js"Value: ".concat(42, true, null, undefined); // "Value: 42truenullundefined"
链式调用支持:
js"JS".concat(" is") .concat(" awesome") .concat("!"); // "JS is awesome!"
示例:
基本使用:
js// 基础示例 "Hello".concat(" ", "World"); // "Hello World" "前端".concat("开发"); // "前端开发"
模板字符串替代:现代语法更简洁
js// 现代语法更简洁 const name = "John"; `Hello ${name}`; // 优于 "Hello".concat(name)
repeat()
string.repeat():(count)
,ES2015,用于将字符串重复指定次数后返回新字符串。返回新字符串。
count:
number
,范围:0-∞
,重复次数。浮点数
:向下取整。负数/无穷数/未提供
:报错(RangeError/RangeError/TypeError)。
返回:
newStr:
string
,新字符串,原始字符串重复count
次的结果。
核心特性:
不修改原字符串
空字符串处理
支持 Unicode 字符
执行性能高:
js// 大量重复:引擎优化后仍高效 "0".repeat(1e6); // 现代引擎可处理
示例:
基本使用:
js// 基础示例 "Hi".repeat(3); // "HiHiHi" "🚀".repeat(2); // "🚀🚀" "0".repeat(5); // "00000"
trim()
string.trim():()
,ES2015,用于移除字符串两端的空白字符(包括空格、制表符
\t\v
、换行符\n
等)。返回新字符串。
返回:
newStr:
string
,新字符串,移除两端空白字符后的结果。
示例:
基本使用:
js// 基础示例 " Hello ".trim(); // "Hello" "\tText\n".trim(); // "Text" " 全角空格 ".trim(); // "全角空格"(不移除)
trimStart()
string.trimStart():()
,ES2019,用于移除字符串开头的空白字符(包括空格、制表符、换行符等)。返回新字符串,别名 trimLeft()
。
返回:
newStr:
string
,新字符串,移除开头空白字符后的结果。
示例:
基本使用:
js// 基础示例 " Hello".trimStart(); // "Hello" "\tText\n".trimStart(); // "Text\n" " 全角空格".trimStart(); // "全角空格"(保留)
trimEnd()
string.trimEnd():()
,ES2019,用于移除字符串结尾的空白字符(包括空格、制表符、换行符等)。返回新字符串,别名 trimRight()
。
返回:
newStr:
string
,新字符串,移除结尾空白字符后的结果。
示例:
基本使用:
js// 基础示例 "Hello ".trimEnd(); // "Hello" "Text\t\n".trimEnd(); // "Text" "全角空格 ".trimEnd(); // "全角空格"(保留)
padStart()
string.padStart():(targetLength, padString?)
,ES2017,用于在字符串开头填充指定字符直到字符串达到目标长度。返回新字符串。
- targetLength:
number
,填充后的目标长度(整数)。 - padString?:
string
,默认:' '
,填充字符。特殊值:多字符
:截断填充。"A".padStart(3, "123")
→"12A"
(取前两位)非字符串
:转换为字符串。"A".padStart(3, true)
→"trA"
(取前两位)
- 返回:
- newStr:
string
,新字符串,填充到目标长度后的结果。
核心特性:
填充规则:
填充长度 = targetLength - 原字符串长度
js// 填充长度 = targetLength - 原字符串长度 "X".padStart(3); // " X"(填充两个空格) "XX".padStart(3, "Y"); // "YXX"(填充一个"Y")
多字符填充截断,从前往后取字符。
js// 多字符填充截断 "ID".padStart(4, "123"); // "12ID" → 需要3字符填充,取"123" "ID".padStart(5, "12"); // "121ID" → 重复填充"12"("12"+"1")
Unicode 支持:
性能问题:大量填充:考虑预生成模板
js// 少量填充:直接使用 padStart() "1".padStart(3, "0"); // "001" // 大量填充:考虑预生成模板 const ZERO_10K = "0".repeat(10000); function padLarge(num, len) { const str = num.toString(); return (ZERO_10K + str).slice(-len); }
示例:
基本使用:
js// 基础示例 "5".padStart(3, "0"); // "005" "Hi".padStart(10, "-"); // "--------Hi"
padEnd()
string.padEnd():(targetLength, padString?)
,ES2017,用于在字符串结尾填充指定字符直到字符串达到目标长度。返回新字符串。
targetLength:
number
,填充后的目标长度(整数)。padString?:
string
,默认:' '
,填充字符。特殊值:多字符
:截断填充。"A".padEnd(3, "123")
→"A12"
(取前两位)非字符串
:转换为字符串。"A".padEnd(3, true)
→"Atr"
(取前两位)
返回:
newStr:
string
,新字符串,填充到目标长度后的结果。
核心特性:类似 padStart()
填充规则:
填充长度 = targetLength - 原字符串长度
多字符填充截断,从前往后取字符。
Unicode 支持:
性能问题:大量填充:考虑预生成模板
示例:
基本使用:
js// 基础示例 "5".padEnd(3, "0"); // "500" "Hi".padEnd(10, "-"); // "Hi--------"
正则表达式相关
match()
string.match():(regexp)
,支持正则,用于检索字符串中与正则表达式匹配的结果。
regexp:
RegExp
,正则表达式对象或可转换为正则的值。返回:
matchedArr:
array|null
,匹配结果(格式取决于g
标志)。- 非全局匹配:返回第一个匹配的详细信息(包含分组信息)。
- 全局匹配:返回所有匹配的子字符串数组。
核心特性:
返回值详解:
非全局匹配(无 g 标志):
jsconst result = "2023-06-16".match(/(\d{4})-(\d{2})-(\d{2})/); /* [ "2023-06-16", // 完整匹配 "2023", // 分组1 "06", // 分组2 "16", // 分组3 index: 0, // 匹配起始位置 input: "2023-06-16", // 原始字符串 groups: undefined // 命名分组对象 ] */
全局匹配(有 g 标志):
js"a1b2c3".match(/\d/g); // ["1", "2", "3"]
命名分组支持(ES2018):
jsconst result = "John Doe".match(/(?<first>\w+) (?<last>\w+)/); /* [ "John Doe", "John", "Doe", index: 0, input: "John Doe", groups: { first: "John", last: "Doe" } ] */
匹配失败:
js"abc".match(/\d/); // null
Unicode 处理:
代理对字符
js"🚀火箭".match(/./gu); // ["🚀", "火", "箭"](正确拆分)
Unicode 属性转义
js"café".match(/\p{L}/gu); // ["c", "a", "f", "é"](匹配所有字母)
示例:
基本使用:
js// 基础示例 "Hello World".match(/o/g); // ["o", "o"](全局匹配) "ID: 123".match(/\d+/); // ["123", index: 4, input: "ID: 123", groups: undefined]
解析复杂字符串:
jsfunction parseLogEntry(entry) { const match = entry.match(/\[(\d{2}:\d{2})\] (\w+): (.+)/); if (!match) return null; return { time: match[1], level: match[2], message: match[3] }; } parseLogEntry("[12:30] ERROR: File not found"); // { time: "12:30", level: "ERROR", message: "File not found" }
进阶扩展:
多语言单词提取:
jsfunction extractWords(text) { return text.match(/\p{L}+/gu) || []; // 匹配多语言中的单词(字) } extractWords("こんにちは! Hello! 你好!"); // ["こんにちは", "Hello", "你好"]
粘滞模式:
\y
jsconst regex = /\d+/y; regex.lastIndex = 5; "abc123def".match(regex); // ["3"](从索引5开始)
matchAll()
string.matchAll():(regexp)
,ES2020,支持正则,必须包含g
,用于返回包含所有匹配正则表达式的结果及分组捕获组的迭代器。
regexp:
RegExp
,正则表达式对象(必须包含g
标志)。返回:
result:
Iterator
,迭代器,生成每个匹配的完整结果对象。
核心特性:
对比 match():
提供了更完整的匹配信息(即使使用全局标志)
jsconst matches = "a1b2c3".matchAll(/(\w)(\d)/g); Array.from(matches); /* [ ["a1", "a", "1", index: 0, input: "a1b2c3", groups: undefined], ["b2", "b", "2", index: 2, input: "a1b2c3", groups: undefined], ["c3", "c", "3", index: 4, input: "a1b2c3", groups: undefined] ] */
必须包含 g 标志:否则抛出
TypeError
返回值详解:迭代器生成的对象包含:
- 匹配数组:完整匹配 + 捕获组
- 标准属性:
index
:匹配起始位置input
:原始字符串groups
:命名捕获组对象
- 新增属性(ES2022):
indices
:匹配索引范围数组indices.groups
:命名分组索引
jsconst result = "2023-06-16".matchAll(/(?<year>\d{4})-(?<month>\d{2})/gd); const firstMatch = result.next().value; /* { 0: "2023-06", 1: "2023", 2: "06", index: 0, input: "2023-06-16", groups: { year: "2023", month: "06" }, indices: [ [0, 7], [0, 4], [5, 7], groups: { year: [0,4], month: [5,7] } ] } */
示例:
提取复杂数据:
jsfunction extractData(text) { const regex = /(?<name>\w+):\s*(?<value>\d+)/gd; return Array.from(text.matchAll(regex), match => match.groups); } extractData("A: 10, B: 20"); // [{ name: "A", value: "10" }, { name: "B", value: "20" }]
进阶扩展:
使用迭代而非数组转换:效率更高
js// 低效:Array.from(matches) for (const match of text.matchAll(regex)) { processMatch(match); // 直接处理 }
粘滞模式优化性能:从指定位置开始
jsconst regex = /word/gy; regex.lastIndex = 100; // 从指定位置开始 text.matchAll(regex);
索引范围处理(ES2022+):
jsfunction getMatchText(match) { const [start, end] = match.indices[0]; return match.input.slice(start, end); }
search()
string.search():(regexp)
,支持正则,用于检索字符串中匹配正则表达式的第一个位置。与 indexOf()
类似但支持正则表达式。
regexp:
string|RegExp
,正则表达式对象或可转换为正则的值。返回:
index:
number
,首个匹配的起始索引(0起)或-1
(未匹配)。
核心特性:
始终返回首个匹配位置:
g
无效。js// 即使使用 g 标志也返回第一个匹配 "a1b2c3".search(/\d/g); // 1(不会返回后续匹配位置)
空搜索值行为:
js"abc".search(""); // 0(空字符串匹配开头) "abc".search(); // 0(未提供参数视为空正则)
对比 indexOf():
特性 search()
indexOf()
参数类型 正则/字符串 仅字符串 正则支持 ✅ ❌ 全局搜索 ❌(仅首个匹配) ❌(需手动循环) 性能 略慢(需正则解析) 更快 支持Unicode
示例:
基本使用
js// 基础示例 "JavaScript".search(/Script/); // 4(匹配起始位置) "Hello World".search(/\d/); // -1(未找到数字)
进阶扩展:
多语言文本检测:
jsfunction isCJK(text) { return text.search(/[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}]/u) !== -1; } isCJK("日本語"); // true
提取匹配边界:
jsfunction getFirstMatchRange(text, regex) { const start = text.search(regex); if (start === -1) return null; const end = start + text.slice(start).match(regex)[0].length; return [start, end]; } getFirstMatchRange("ID: 123", /\d+/); // [4, 7]
replace()
string.replace():(searchValue, replaceValue)
,支持正则,用于替换字符串中匹配的子串。返回新字符串,支持字符串替换和正则表达式替换,并允许使用函数进行高级处理。
searchValue:
string|RegExp
,要替换的子串或正则表达式。以'apple'
为例:字符串
:仅替换第一个匹配项。"p"
→ 替换第一个 "p"正则表达式
:根据标志替换(g
全局替换)。/p/g
→ 替换所有 "p"非正则对象
:转换为字符串。123
→ 匹配 "123"
replaceValue:
string|function
,替换文本或替换函数。以"2023"
为例:字符串
:直接替换(支持特殊替换模式:$&
,$1
等)。"Year: $&"
→ "Year: 2023"替换函数
:见替换函数详解,动态生成替换内容(参数:匹配文本、分组等)。(match) => +match + 1
→ "2024"空值
:转换为字符串。null
→ "null"
返回:
newStr:
string
,新字符串,替换完成的结果。
核心特性:
特殊替换模式:
模式 描述 示例( "John Doe"
替换为"$2, $1"
)$$
插入 "$" 字符 "$$1"
→"$1"
$&
插入匹配的子串 "Mr. $&"
→"Mr. John Doe"
$`
插入匹配子串前的文本 "Hello World!".replace(/(World)/, "[$`]")
"[$`]"
→"[Hello ]"
(匹配 "World" 时)$'
插入匹配子串后的文本 "Hello World!".replace(/(World)/, "[$']")
"[$']"
→"[!]"
(匹配 "World" 时)$n
插入第 n 个分组(n=1-100) /(\w+) (\w+)/
→"$2, $1"
→"Doe, John"
$<name>
插入命名分组(ES2018+) /(?<first>\w+) (?<last>\w+)/
→"$<last>, $<first>"
替换函数详解:
js(match, p1, p2, ..., offset, string, groups) => newString
- match:
string
,匹配的子串(相当于$&
)。 - p1, p2, ...:
string
,分组捕获内容(按顺序对应$1
,$2
)。 - offset:
number
,匹配子串在原字符串中的偏移量。 - string:
string
,原始字符串。 - groups:
object
,命名捕获组对象(ES2018+)。
js// 函数替换示例 "border-top".replace(/-(\w)/g, (_, char) => char.toUpperCase()); // "borderTop"(转驼峰命名)
- match:
示例:
基本使用:
js// 基础示例 "Hello World".replace("World", "JavaScript"); // "Hello JavaScript"
全局替换:
js"cat, dog, cat, bird".replace(/cat/g, "dog"); // "dog, dog, dog, bird"
分组引用:特殊替换模式
js"2023-06-16".replace(/(\d{4})-(\d{2})-(\d{2})/, "$2/$3/$1"); // "06/16/2023"
进阶扩展:
转义 HTML:
jsfunction escapeHTML(str) { return str.replace(/[&<>"']/g, char => ({ "&": "&", "<": "<", ">": ">", "\"": """, "'": "'" }[char])); } escapeHTML(`<div>"Hi"</div>`); // "<div>"Hi"</div>"
千分位数字:
jsconst num = "323123213.32232"; const result = num.replace(/^(\d+)(\.\d+)$/, (match, integer, decimal) => { // 对整数部分应用千分位分隔符 const formattedInteger = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ","); // 组合整数部分和小数部分 return formattedInteger + decimal; }); console.log(result); // 输出: "323,123,213.32232"
命名分组支持:
js"2023-06".replace(/(?<year>\d{4})-(?<month>\d{2})/, "$<month>/$<year>"); // "06/2023"
Unicode 安全替换
js"🚀a🚀".replace(/\p{Emoji}/gu, "❤️"); // "❤️a❤️"(需 u 标志)
函数替换高级用法
js"a1 b2 c3".replace(/(\w)(\d)/g, (match, letter, num) => String.fromCharCode(letter.charCodeAt(0) + parseInt(num)) ); // "b d f"
替换特殊字符
js"$100".replace(/\$/g, "$$$$"); // "$$100"(转义 $ 字符)
replaceAll()
string.replaceAll():(searchValue, replaceValue)
,ES2021,支持正则,用于全局替换字符串中所有匹配的子串。无需正则表达式即可实现全量替换。
searchValue:
string|RegExp
,要替换的子串或带g
标志的正则表达式。- 注意:当
searchValue
为字符串时,全局替换所有匹配项。 - 注意:当
searchValue
为正则时,必须包含g
标志(否则抛出TypeError
)。
- 注意:当
replaceValue:
string|(match,p1,...,offset,string)=>string
,替换文本或替换函数。- 注意:相比
replace()
,不接收分组参数(因字符串搜索无分组)。 - 注意:正则搜索时函数参数与
replace()
相同(含分组)。
- 注意:相比
返回:
newStr:
string
,新字符串,所有匹配项替换后的结果。
核心特性:
- 对比 replace():
- 全局字符串替换:不需要使用正则。
- 首个字符串替换:需额外处理。
- 正则搜索必须包含
g
:否则会抛出错误。 - 函数参数:替换函数不接收分组参数groups。
示例:
简单全局替换:
jsconst text = "Error: not found; Error: timeout"; text.replaceAll("Error", "Warning"); // "Warning: not found; Warning: timeout"
替换函数:
jsconst data = { red: "#FF0000", green: "#00FF00" }; "red,green,blue".replaceAll(/\w+/g, color => data[color] || color); // "#FF0000,#00FF00,blue"
进阶扩展:
替换函数高级用法:
js"a1 b2 c3".replaceAll(/\d/g, (match, offset) => offset % 2 === 0 ? match : "X" ); // "aX b2 cX"
转义特殊字符:
jsfunction escapeRegExp(string) { return string.replaceAll(/[.*+?^${}()|\[\]\\]/g, '\\$&'); } const searchText = escapeRegExp("file.txt"); new RegExp(searchText); // 安全创建正则
split()
string.split():(separator?, limit?)
,支持正则,用于将字符串分割为子字符串数组。支持限制返回数组长度。
separator?:
string|RegExp
,分割点(字符串或正则),未提供时默认返回整个字符串的数组。以"a-b-c"
为例:字符串
:按字面值分割。"-"
→["a","b","c"]
空字符串
:每个字符分割。""
→["a","-","b","-","c"]
正则表达式
:按匹配模式分割。/[-]/
→["a","b","c"]
未提供
:返回包含整个字符串的数组。( )
→["a-b-c"]
不匹配值
:返回包含整个字符串的数组。"x"
→["a-b-c"]
limit?:
number
,返回数组的最大长度(超过部分被丢弃)。返回:
arr:
array
,数组,分割后的子字符串集合。
核心特性:
空分隔符行为:每个字符分割
js"hello".split(""); // ["h","e","l","l","o"] "🚀火".split(""); // ["\uD83D", "\uDE80", "火"](代理对拆分)
捕获组处理(正则):捕获的分组也会返回
js"2023-06-16".split(/(-)/); // ["2023", "-", "06", "-", "16"]
Unicode 安全分割:使用 u 标志避免代理对拆分
js// 默认代理对拆分 "🚀火".split(""); // ["\uD83D", "\uDE80", "火"] // 使用 u 标志避免代理对拆分,(?:) 表示捕获空值 "🚀火".split(/(?:)/u); // ["🚀", "火"]
示例:
基本使用:
js// 基础示例 "apple,orange,banana".split(","); // ["apple", "orange", "banana"] "2023-06-16".split(/-/); // ["2023", "06", "16"]
移除空项:
js"a,,b".split(",").filter(s => Boolean(s)); // ["a", "b"]
其他
localeCompare()
string.localeCompare():(compareString,locales?,options?)
,用于根据当前语言环境比较两个字符串的排序顺序。提供本地化的字符串排序能力,支持多语言排序规则(如德语、中文等)。
compareString:
string
,被比较的字符串。locales?:
string|array
,语言标签(字符串或数组),如"de"、"zh"、"en"
等。options?:
object
,配置对象(敏感度、大小写处理等)。- sensitivity:
base|accent|case|variant,
默认:variant`,比较敏感度。 - ignorePunctuation:
boolean
,默认:false
,是否忽略标点。 - numeric:
boolean
,默认:false
,是否按数字顺序("10" > "2")。 - caseFirst:
upper|lower|false
,默认:false
,大小写优先级。 - usage:
sort|search
,默认:sort
,排序或搜索模式。
- sensitivity:
返回:
result:
number
,负数
(前<后),0
(相等),正数
(前>后)。
核心特性:
语言敏感排序:
js// 德语:ä 在 z 前 "ä".localeCompare("z", "de"); // -1 // 瑞典语:ä 在 z 后 "ä".localeCompare("z", "sv"); // 1
数字排序:
js// 普通排序 "10".localeCompare("2"); // -1("10" 在 "2" 前,按字符串) // 数字模式排序 "10".localeCompare("2", undefined, { numeric: true }); // 1(10 > 2)
大小写处理:默认大写字母排后。
js// 默认大小写敏感 "A".localeCompare("a"); // >0(大写字母排后) // 忽略大小写 "A".localeCompare("a", undefined, { sensitivity: "base" }); // 0
示例:
基本使用:
js// 基础示例 "ä".localeCompare("z", "de"); // -1(德语中 ä 排在 z 前) "二".localeCompare("一", "zh"); // 1(中文数字排序)
用户列表按姓名字母排序:
jsconst users = [ { name: "张伟" }, { name: "王芳" }, { name: "李娜" } ]; users.sort((a, b) => a.name.localeCompare(b.name, "zh-CN") ); // 李娜, 王芳, 张伟(中文拼音顺序)
文件名数字排序:
jsconst files = ["file10", "file2", "file1"]; files.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }) ); // ["file1", "file2", "file10"]
normalize()
string.normalize():(form?)
,ES2015,用于将字符串转换为 Unicode 标准化形式。它解决字符的多种表示方式问题(如组合字符 vs 预组合字符)。
form?:
NFC|NFD|NFKC|NFKD
,默认:NFC
,标准化形式。NFC
:Normalization Form C (Composition)
,默认,先分解再组合,优先使用预组合字符(如é
而非e + ´
)。NFD
:Normalization Form D (Decomposition)
,完全分解:分离基本字符和组合标记(如é
→e + ´
)。NFKC
:NFK Casefold Composition
,兼容组合:移除格式差异(如ff
→ff
)。NFKD
:NFK Casefold Decomposition
,兼容分解:移除格式差异并分解字符。
返回:
newStr:
string
,新字符串,Unicode 标准化后的结果。
核心特性:
解决等价表示问题:组合字符序列 vs 预组合字符
js// 组合字符序列 vs 预组合字符 const composed = "é"; // U+00E9 const decomposed = "e\u0301"; // U+0065 + U+0301 composed === decomposed; // false composed.normalize() === decomposed.normalize(); // true(NFC 下)
兼容形式处理:
js// 连字标准化 "ffi".normalize("NFKC"); // "ffi" // 全角数字标准化 "123".normalize("NFKC"); // "123"
不修改原字符串:
js// 不修改原字符串 const str = "cafe\u0301"; str.normalize(); // "café" console.log(str); // "café"(未变)
Unicode 标准化形式对比
形式 类型 字符存储方式 典型用途 NFC 组合 尽量使用预组合字符 显示、存储 NFD 分解 分离基本字符+组合标记 文本处理、搜索 NFKC 兼容组合 移除格式差异并组合 标识符比较、数据清洗 NFKD 兼容分解 移除格式差异并分解 拼音处理、深度文本分析
示例:
基本使用:
js// 基础示例 "café".normalize(); // "café"(NFC 形式) "cafe\u0301".normalize(); // "café"(组合字符转预组合)
进阶扩展:
用户名规范化:
jsfunction normalizeUsername(name) { return name.normalize("NFKC") .replace(/\p{Mark}/gu, "") // 移除所有组合标记 .toLowerCase(); } normalizeUsername("Jŏhņ_ß"); // "john_ss"
拼音处理:
jsfunction getPinyinTones(text) { return text.normalize("NFD") .replace(/[\u0300-\u036f]/g, tone => ({ "\u0304": "1", "\u0301": "2", "\u030C": "3", "\u0300": "4" }[tone] || "") ); } getPinyinTones("mā má mǎ mà"); // "ma1 ma2 ma3 ma4"
长度差异处理:
js"é".length; // 1 "é".normalize("NFD").length; // 2
代理对保留
js"🚀".normalize("NFD"); // "🚀"(代理对不受影响)
正则表达式结合
jsconst regex = /^[\p{L}\p{N}]+$/u; function isAlphanumeric(str) { return regex.test(str.normalize("NFKD")); } isAlphanumeric("ABC123"); // true(全角转半角)