导读:本期聚焦于小伙伴创作的《怎么通过 String.codePointAt() 正确处理包含特殊表情符号的 Unicode 字符》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《怎么通过 String.codePointAt() 正确处理包含特殊表情符号的 Unicode 字符》有用,将其分享出去将是对创作者最好的鼓励。

在JavaScript的字符串处理场景中,包含特殊表情符号的字符串经常会出现截取异常、长度计算错误的问题,这背后和Unicode的编码规则以及JavaScript的字符串存储方式有关,String.codePointAt()方法就是解决这类问题的核心工具。

怎么通过 String.codePointAt() 正确处理包含特殊表情符号的 Unicode 字符

Unicode编码与JavaScript字符串的基础关系

Unicode编码将字符分为两个平面:基本多文种平面(BMP)和辅助平面。BMP内的字符码点范围是U+0000到U+FFFF,JavaScript用单个16位UTF-16编码单元存储,比如普通的英文字母、汉字都属于这个范畴。而辅助平面的字符码点范围是U+10000到U+10FFFF,比如大部分表情符号、部分生僻汉字,这类字符需要用两个16位UTF-16编码单元存储,也就是我们常说的代理对。

JavaScript的字符串本质上是一个UTF-16编码单元的序列,所以当我们用传统的charAt()length属性处理辅助平面字符时,就会把代理对拆成两个独立的单元处理,导致结果错误。

charAt与codePointAt的核心差异

我们先通过对比两个方法的表现,来看codePointAt的优势:

  • charAt(index):返回指定索引位置的UTF-16编码单元,对于辅助平面字符,只能返回代理对的前半部分或者后半部分,无法得到完整字符。
  • codePointAt(index):返回指定索引位置字符的完整Unicode码点,如果索引位置是代理对的前半部分,会直接返回整个辅助平面字符的码点;如果索引位置是代理对的后半部分,会返回后半部分的码点。

简单示例对比

我们用一个表情符号做测试,比如表情😀的码点是U+1F600,属于辅助平面字符:

// 定义包含表情的字符串
const str = "😀a";
// 查看字符串的length,代理对占两个单元,所以长度是3
console.log(str.length); // 输出3

// 用charAt获取第一个字符,只能拿到代理对的前半部分
console.log(str.charAt(0)); // 输出乱码,因为只拿到了半个字符

// 用codePointAt获取第一个字符的码点
console.log(str.codePointAt(0)); // 输出128512,对应U+1F600,也就是😀的完整码点

// 用codePointAt获取第二个字符的码点,第二个索引是代理对的后半部分
console.log(str.codePointAt(1)); // 输出56832,是代理对后半部分的码点

用codePointAt正确处理表情符号的常用场景

场景1:正确计算包含表情的字符串长度

传统的str.length会把表情符号算成2个字符,我们可以通过遍历码点的方式计算真实长度:

function getRealLength(str) {
    let length = 0;
    for (let i = 0; i < str.length; i++) {
        const code = str.codePointAt(i);
        // 辅助平面字符的码点大于0xFFFF,占两个UTF-16单元,索引需要多跳一位
        if (code > 0xFFFF) {
            length++;
            i++;
        } else {
            length++;
        }
    }
    return length;
}

const testStr = "你好😀世界";
console.log(testStr.length); // 输出6,传统长度错误
console.log(getRealLength(testStr)); // 输出5,真实长度正确

场景2:正确截取包含表情的字符串

截取字符串时如果直接按索引切,可能会把表情符号的代理对拆开,我们可以用codePointAt判断字符范围,实现安全截取:

function safeSubstring(str, start, end) {
    let realStart = 0;
    let realEnd = 0;
    let count = 0;
    // 先找到真实的起始索引
    for (let i = 0; i < str.length && count < start; i++) {
        const code = str.codePointAt(i);
        if (code > 0xFFFF) {
            i++;
        }
        count++;
        realStart = i + 1;
    }
    count = 0;
    // 再找到真实的结束索引
    for (let i = realStart; i < str.length && count < (end - start); i++) {
        const code = str.codePointAt(i);
        if (code > 0xFFFF) {
            i++;
        }
        count++;
        realEnd = i + 1;
    }
    return str.slice(realStart, realEnd);
}

const subStr = "测试😀表情截取";
console.log(safeSubstring(subStr, 0, 3)); // 输出测试😀,截取正确
console.log(subStr.substring(0, 3)); // 输出测试�,截取错误,拆了代理对

场景3:遍历包含表情的字符串

遍历字符串时如果需要拿到完整字符,可以结合codePointAt和String.fromCodePoint()方法:

function iterateStr(str) {
    const result = [];
    for (let i = 0; i < str.length; i++) {
        const code = str.codePointAt(i);
        if (code > 0xFFFF) {
            // 辅助平面字符,拼接完整字符
            result.push(String.fromCodePoint(code));
            i++; // 跳过代理对的后半部分
        } else {
            result.push(String.fromCodePoint(code));
        }
    }
    return result;
}

const iterateTest = "😀abc😁";
console.log(iterateStr(iterateTest)); // 输出["😀","a","b","c","😁"],每个元素都是完整字符

注意事项

  • 调用codePointAt(index)时,如果index超出了字符串的长度,会返回undefined。
  • 如果index位置是代理对的后半部分,返回的码点会落在0xDC00到0xDFFF之间,属于代理对专用范围,此时这个码点没有对应独立字符,需要结合前半部分处理。
  • 码点转字符的时候要用String.fromCodePoint(),不要用String.fromCharCode(),后者无法处理辅助平面的码点。
处理Unicode字符时,只要涉及辅助平面的内容,优先使用codePointAt相关的方法,避免用传统的charAt、length、substring直接操作,就能避免大部分表情符号处理的问题。

String_codePointAtUnicode表情符号码点修改时间:2026-07-07 19:21:35

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。