string.prototype.at() 是 es2022 引入的安全访问字符串字符的方法,按逻辑字符(grapheme)而非 utf-16 代码单元索引,自动处理代理对,支持负索引且越界返回 undefined。

String.prototype.at() 是 ES2022 引入的原生方法,专为安全访问字符串中任意位置的字符而设计,能天然规避由代理对(surrogate pairs)引发的索引偏移问题。
为什么传统方括号索引会出错
JavaScript 字符串以 UTF-16 编码存储,大多数常用字符(如 ASCII、常见汉字)占 1 个代码单元(code unit),但部分 Unicode 字符(如 emoji、古文字、某些表情符号)需要 2 个连续的代码单元组成一个代理对。例如 '' 实际占 2 个 code unit,'' 占 2 个,'' 占 4 个(两个代理对)。
使用 str[0] 或 str.charAt(0) 是按 code unit 索引的,不是按“用户感知的字符”索引。当你想取第 1 个 emoji,却用 str[1],很可能拿到代理对的后半部分——结果是无效的 Unicode 字符(如 '\ud83d'),甚至显示为 。
at() 的工作方式:按“字符”而非“代码单元”定位
at() 内部基于 String.prototype.codePointAt() 和字符边界识别,自动跳过代理对的中间位置,确保每次返回的是一个完整的 Unicode 字符(即一个逻辑字符,也称“grapheme”层面的单位)。
- 正向索引从 0 开始,
str.at(0)返回第一个完整字符 - 支持负数索引:
str.at(-1)直接获取最后一个字符,无需str[str.length - 1] - 越界时返回
undefined,比str[999]返回undefined更语义清晰(后者在旧环境可能返回"")
实际对比示例
以字符串 const str = 'Hello ??'; 为例(注意:空格后是两个 emoji,各为一个代理对):
-
str[0]→'H'✅;str[6]→'\ud83d'❌(只是 ? 的高位代理) -
str.at(0)→'H'✅;str.at(6)→'?'✅(自动跨过代理对) -
str.at(-1)→'?'✅;str[str.length - 1]→'\udc31'❌(低位代理,不完整)
兼容性与替代方案
at() 在现代浏览器和 Node.js ≥16.6 中已原生支持。若需兼容旧环境:
- 可用
Array.from(str)[index]——Array.from会正确拆分代理对,生成每个完整字符的数组 - 或手动封装:
function at(str, index) { const arr = Array.from(str); return arr[index >= 0 ? index : arr.length + index]; } - 避免使用
for (let i = 0; i 遍历字符,改用 <code>for (const char of str)或Array.from(str).forEach(...)










