Get Intrinsic S1
中文标题:获取内在属性提案
- 阶段: Stage 1
- 状态: 进行中
- ECMAScript 版本: —
- 同步时间: 2026年8月26日
- English original · 官方仓库
该提案旨在为首次运行的代码提供一种稳健的方式安全地访问内在属性(内置对象),解决后续代码修改环境的风险。它提议增加 Reflect.getIntrinsic 通过字符串名称获取内在属性,避免使用 eval,并提供 Reflect.getIntrinsics 枚举可用名称。
以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。
"获取内在属性" 提案
ECMAScript 提案、规范和参考实现,旨在提供一种稳健的方式来为首次运行的代码获取内在属性。
规范由 @ljharb 起草。
此提案目前处于流程的阶段 1。
"首次运行" 代码
整个提案假设 JavaScript 的一个公理:即大部分语言功能只有在先前运行的代码未做任何恶意操作时才可被信赖。换句话说,该提案的所有用例都假设运行时 JavaScript 环境的状态与代码首次评估时作者的预期相匹配(这些预期可能是"主机提供的没有改变"、"缺失/损坏的功能已被 shim/polyfill 填补"、"内置对象被锁定,类似于 SES"等)。
理由 / 问题陈述
当任何一段代码被评估时,它可能会创建稍后执行的函数。在函数创建时,环境可以被信赖(根据上述)。然而,在函数执行时,这一预期可能不再成立,因为后面的代码可能以意外的方式修改了环境(广告代码、浏览器扩展等)。
下面是一个人为的例子,展示了一个包容易受到后续运行代码修改内置对象的影响:
包作者通常采用的方法以使代码对后续修改具有鲁棒性,就是"缓存"——将所需的函数副本存储在闭包词法变量中。请注意,以这种方式借用原型方法需要使用 .call 来设置接收者("this" 值),因此为了对此也具有鲁棒性,需要"调用绑定"这些方法:
(请注意,这种模式并不符合人体工程学,本提案并不试图改善这里的人体工程学——那将由其他提案处理)
在一个应用中,通常有许多这类包(通过大型框架/库的传递依赖)。由于代码拆分和懒加载,这些包常常不会同时被评估,从而打破了上面建立的"首次运行"预期。为了最小化出现问题,它们通常使用一个共同的"共享"包来持有所有内在属性。我的 es-shims 包都使用一个包来实现这一点,即 get-intrinsic,目前每周约有 1510 万次下载。
这意味着无论哪个 shim 最先被评估,都会导致 get-intrinsic 首先被评估,从而为所有其他 shim(即使在环境被更改后加载的 shim)提供安全、鲁棒的内在属性访问。这是使用该 npm 包(通过另一个包 call-bind(每周约 1820 万次下载))修改后的示例代码:
这个 get-intrinsic 抽象的成本是 9.7KB,发送给依赖这段代码的任何部分的所有浏览器。此外,由于并非每个内在属性都可以从全局对象访问——有些需要现代语法,这些语法在所有浏览器中可能无法解析——因此需要某种形式的 eval 来获取这些内在属性,这与某些网站的 CSP 要求冲突。
另请参阅委员会在 2021 年 7 月 关于此提案的讨论。
提议的解决方案
在 Reflect 上提供一个函数属性 getIntrinsic,它接受一个字符串参数,并有效复制 get-intrinsic 包的 API,但明确不暴露规范的内部 %a.b.c% 表示法。它将返回所请求的"原始"值。
添加新方法的 shim/polyfill 当然也需要包装/替换 getIntrinsic 函数,以便它也能提供新方法;像 SES 这样的"锁定"库同样需要包装/替换 getIntrinsic 函数,以便它提供它希望的任何形式的方法。
在提供此 API 的现代浏览器/引擎中,此成本将降至零,并且 eval/CSP 冲突将消失。
此外,提供了 Reflect.getIntrinsics,它产生一个"内在属性名称"的迭代器——每个名称都可以传递给 Reflect.getIntrinsic 以获取所需的值。
规范
您可以查看 Reflect.getIntrinsic/Reflect.getIntrinsics 解决方案的规范,渲染为 HTML。
实现
目前还没有——在阶段 3 之前发布语言提案 API 的运行时实现是不合适的。