
core-js 中的 Well-formed Unicode Strings 提案实现isWellFormed与toWellFormed深度解析【免费下载链接】core-jsStandard Library项目地址: https://gitcode.com/GitHub_Trending/co/core-js本篇文章以 core-js 仓库中 well-formed-unicode-strings.md 文档为骨架系统讲解 TC39 的 Well-formed Unicode strings 提案在 core-js 中的完整落地从String.prototype.isWellFormed与String.prototype.toWellFormed的 API 签名与入口点到packages/core-js/modules下的逐位级源码实现、Safari 兼容补丁、QUnit 测试用例以及各发行层级es / stable / actual / full / virtual的引入方式。读完本文你将掌握如何在不支持该特性的 JavaScript 引擎上引入这两个方法并理解其底层 UTF-16 判定原理。提案背景为什么需要 Well-formed 字符串JavaScript 的字符串以 UTF-16 编码单元code unit存储。绝大多数 Unicode 字符可以映射为单个 UTF-16 编码单元但 BMP 之外的字符如 emoji码点 U1F4A9需要由**一对代理项surrogate pair**表示高代理项leading/high surrogate范围\uD800\uDBFF低代理项trailing/low surrogate范围\uDC00\uDFFF当字符串中只出现高代理项或只出现低代理项、或两者顺序颠倒时就产生了未配对的代理项unpaired surrogate / lone surrogate这样的字符串被定义为 ill-formed格式不佳。这类字符串在传统处理中容易引发问题例如encodeURI会抛出URIErrorURL解析可能失败传给 Web API 或写入存储时可能产生无效数据。TC39 的 Well-formed Unicode strings 提案为此给String.prototype增加了两个零参数方法用于检测与修复这种状态随后该特性随 ES2024 正式标准化。在 core-js 中它被收录为 stage 4 提案并默认随稳定入口提供。内置签名与核心 API原文档给出的类型签名如下class String { isWellFormed(): boolean; toWellFormed(): string; }String.prototype.isWellFormed(): boolean返回当前字符串是否为 well-formed即是否包含未配对的代理项。不修改原字符串无参数。String.prototype.toWellFormed(): string返回一个新的字符串将其中所有未配对的代理项替换为 Unicode 替换字符 UFFFD若字符串本身已 well-formed则原样返回但始终返回新字符串对象。两者的行为都由ToString语义先对this做强制转换因此1会被当作字符串1处理但null、undefined会抛TypeErrorSymbol上下文同样抛错详见下文测试用例。入口点Entry points原文档标注的入口为core-js/proposals/well-formed-unicode-strings在仓库中对应 packages/core-js/proposals/well-formed-unicode-strings.js其内容十分简洁——它只是两个底层模块的聚合入口use strict; // https://github.com/tc39/proposal-is-usv-string require(../modules/esnext.string.is-well-formed); require(../modules/esnext.string.to-well-formed);实际引入方式在安装 core-js 包后于应用入口处引入即可import core-js/proposals/well-formed-unicode-strings; // 或按需只引入其中一个 import core-js/proposals/well-formed-unicode-strings;esnext.string.*两个模块目前仅是过渡别名esnext.string.is-well-formed.js 内部仅转发到es.string.is-well-formed源码注释中标注了TODO: Remove from core-js4——这从侧面印证该提案已完成标准化正式实现统一收敛到es命名空间。源码级实现isWellFormed的位运算判定核心实现位于 packages/core-js/modules/es.string.is-well-formed.jsvar charCodeAt uncurryThis(.charCodeAt); $({ target: String, proto: true }, { isWellFormed: function isWellFormed() { var S toString(requireObjectCoercible(this)); var length S.length; for (var i 0; i length; i) { var charCode charCodeAt(S, i); // single UTF-16 code unit if ((charCode 0xF800) ! 0xD800) continue; // unpaired surrogate if (charCode 0xDC00 || i length || (charCodeAt(S, i) 0xFC00) ! 0xDC00) return false; } return true; } });算法的关键点快速跳过普通单元charCode 0xF800 ! 0xD800利用位掩码判断当前编码单元是否落在代理项区间0xD8000xDFFF。0xF800是高 5 位掩码代理项区间的特征是0xD800到0xDFFF其高 5 位恰好为110110xD800 1101 1000 0000 0000。非代理项单元直接continue这是最常见的分支性能最优。低代理项单独出现charCode 0xDC00说明当前是低代理项却孤身出现立即返回false。高代理项检查后继i length说明高代理项位于字符串末尾、没有后继(charCodeAt(S, i) 0xFC00) ! 0xDC00用0xFC00掩码校验后继单元是否为低代理项。任一条件成立即判定 ill-formed。该实现与规范String.prototype.isWellFormedECMA-262 §22.1.3.19的判定逻辑一一对应复杂度为 O(n)且只读遍历不产生额外分配。源码级实现toWellFormed与 Safari 兼容补丁修复逻辑位于 packages/core-js/modules/es.string.to-well-formed.js其中包含一个值得注意的引擎 bug 检测var $toWellFormed .toWellFormed; var REPLACEMENT_CHARACTER \uFFFD; // Safari bug var TO_STRING_CONVERSION_BUG $toWellFormed fails(function () { return call($toWellFormed, 1) ! 1; }); $({ target: String, proto: true, forced: TO_STRING_CONVERSION_BUG }, { toWellFormed: function toWellFormed() { var S toString(requireObjectCoercible(this)); if (TO_STRING_CONVERSION_BUG) return call($toWellFormed, S); // ... } });这里的fails是 core-js 内部的特性检测辅助若宿主原生toWellFormed无法正确处理数字参数Safari 旧版本把1错误转换而非1则TO_STRING_CONVERSION_BUG为真polyfill 会被forced强制启用并退化为先自行ToString、再调用原生方法的修补路径保证toWellFormed.call(1) 1。polyfill 主路径的修复逻辑与isWellFormed同构只是把未配对的代理项替换为\uFFFDvar result $Array(length); for (var i 0; i length; i) { var charCode charCodeAt(S, i); if ((charCode 0xF800) ! 0xD800) result[i] charAt(S, i); // 普通单元原样拷贝 else if (charCode 0xDC00 || i 1 length || (charCodeAt(S, i 1) 0xFC00) ! 0xDC00) result[i] REPLACEMENT_CHARACTER; // 未配对代理项 → UFFFD else { // 合法代理对 result[i] charAt(S, i); result[i] charAt(S, i); } } return join(result, );实现细节上先按长度预分配数组、逐单元填充、最后join拼接避免字符串拼接的反复重分配对合法代理对一次性处理两个编码单元并同步前进索引确保不重复遍历。行为验证QUnit 测试用例仓库在 tests/unit-global/es.string.is-well-formed.js 与 tests/unit-global/es.string.to-well-formed.js 中提供了完整的 QUnit 断言unit-pure 目录下还有针对 pure 版本不污染全局原型的对应测试。测试覆盖了正常well-formed输入assert.true(isWellFormed.call(a)); assert.true(isWellFormed.call()); // 合法代理对 assert.true(isWellFormed.call(ab)); assert.same(toWellFormed.call(ab), ab); // 已 well-formed 时原样返回ill-formed 输入孤立/错序代理项assert.true(!isWellFormed.call(\uD83D)); // 孤高代理项 assert.true(!isWellFormed.call(\uDCA9)); // 孤低代理项 assert.true(!isWellFormed.call(\uDCA9\uD83D)); // 顺序颠倒 assert.true(!isWellFormed.call(a\uD83Da)); // 嵌入字符串中间 assert.same(toWellFormed.call(\uD83D), \uFFFD); assert.same(toWellFormed.call(\uDCA9\uD83D), \uFFFD\uFFFD); assert.same(toWellFormed.call(a\uD83Da), a\uFFFDa);类型转换与边界// 对象通过 toString 参与强制转换 assert.true(isWellFormed.call({ toString() { return abc; } })); assert.same(toWellFormed.call(1), 1); // 数字 → 字符串 // strict 模式下 null / undefined 抛 TypeError // Symbol 上下文抛错 assert.throws(() isWellFormed.call(Symbol(test)));测试还断言了两个方法的arity 0、函数名正确、looksNative在原生存在时不做无谓覆盖以及原型属性不可枚举。在 core-js 各层级中的分布与引入方式该特性已随 ES2024 标准化因此完整贯穿 core-js 的所有稳定入口层级es 层es/string/is-well-formed.js 与 es/string/to-well-formed.js直接require对应modules/es.string.*实现stable / actual / full 层逐层转发stable → actual → full可直接import core-js/full/string/is-well-formed;virtual 层不污染原型返回可调用函数es/string/virtual/is-well-formed.js、full/string/virtual 等供 core-js-pure 风格调用instance 层以实例方法形式导出es/instance/is-well-formed.js → stable/instance → actual/instance。此外packages/core-js/stage/4.js 中通过require(../proposals/well-formed-unicode-strings)将本特性纳入 stage 4 聚合入口因此直接引入core-js/stage/4也会一并包含这两个方法。姊妹提案Well-formedJSON.stringify与字符串 well-formed 直接相关的还有姊妹提案 Well-formedJSON.stringify其文档见 well-formed-jsonstringify.md入口为core-js/proposals/well-formed-stringify对应 proposals/well-formed-stringify.js仅引入 es.json.stringify。两者的区别在于String.prototype.toWellFormed作用于 JS 字符串本身而 well-formedJSON.stringify解决的是JSON.stringify输出 ill-formed JSON旧引擎会把孤立代理项原样输出为裸字符产生非法的 UTF-8 序列。在 es.json.stringify.js 中可以看到对应的特性检测与修复逻辑// https://github.com/tc39/proposal-well-formed-stringify var ILL_FORMED_UNICODE fails(function () { return $stringify(\uDF06\uD834) ! \\udf06\\ud834 || $stringify(\uDEAD) ! \\udead; });当原生实现不合格时polyfill 会以正则surrogates /[\uD800-\uDFFF]/g扫描序列化结果并通过fixIllFormedJSON把未配对的代理项转义为\udXXX形式的合法 JSON 转义序列而合法的代理对则保持原样。两者协同可以在旧引擎上同时保证字符串与 JSON 输出的 well-formed。实战建议何时需要 polyfill如果你的目标运行时尚未实现 ES2024 的这两个方法如较旧的 Safari、Node 20 等通过core-js/proposals/well-formed-unicode-strings一次性补齐如果只需其中之一可按需引入core-js/stable/string/is-well-formed或to-well-formed更细粒度入口。与现有方案的对比此前处理孤立代理项通常依赖手写正则/(\uD800-\uDBFF)|((?![\uD800-\uDBFF])[\uDC00-\uDFFF])/g之类的方式性能与可读性都不如规范化的isWellFormed/toWellFormedcore-js 的实现采用位运算单遍扫描O(n) 复杂度且无正则回溯风险。安全边界两者都遵循ToString语义——null/undefined会抛TypeErrorSymbol会抛错因此对不确定的输入如 Web API 返回值建议先做类型判断或String()包裹这与源码中的requireObjectCoercible行为一致。【免费下载链接】core-jsStandard Library项目地址: https://gitcode.com/GitHub_Trending/co/core-js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考