core-js 中 ECMAScript Reflect 全面解析:模块清单、内置方法签名与多入口使用指南
本篇指南以 docs/web/docs/features/ecmascript/reflect.md 为骨架,系统梳理 core-js 对 ECMAScript Reflect 内置对象的 polyfill 实现:涵盖 14 个独立模块的完整清单、TypeScript 签名、es | stable | actual | full 四套入口路径的用法,并深入源码剖析每个方法的底层实现与引擎 bug 修复逻辑。读完本文,你将能够按需精确引入任意 Reflect 方法,理解 core-js 如何在不支持的运行环境中忠实还原 Reflect 语义,并能用 Reflect.ownKeys、Reflect.construct 等写出可运行、可验证的实战代码。
一、为什么 core-js 需要 polyfill Reflect
Reflect 是 ES2015(ES6)引入的全局内置对象,提供了与 Object 上对应静态方法一一映射的反射语义操作,但它并非构造函数,不能 new。两者最直观的区别在于返回值约定:Object.defineProperty 失败时抛异常,而 Reflect.defineProperty 返回 false;delete 运算符在严格模式下对不可配置属性会抛错,而 Reflect.deleteProperty 返回 false。此外 Reflect 还提供 Object 没有的方法(如 Reflect.construct、Reflect.ownKeys、Reflect.apply)。
core-js 的定位是"标准库 polyfill",它通过 packages/core-js/modules/ 目录下一系列 es.reflect.*.js 模块,在旧引擎(尤其是 MS Edge 旧版本、旧版 Firefox 等)缺失或不完整实现 Reflect 时逐方法补齐。每个模块都通过 internals/export 以 { target: 'Reflect', stat: true } 的方式把方法挂到 Reflect 上,并针对已知引擎 bug 做 forced(强制覆盖)与 sham(模拟实现)标记——这一点在下面的源码剖析中会反复出现。
二、Modules:14 个 Reflect 模块完整清单
原文档列出 core-js 为 Reflect 提供的全部模块,每个模块独立成文件、可单独引入,文件均位于 packages/core-js/modules/:
| 方法 | 对应模块文件 | 作用 |
|---|---|---|
Reflect.apply |
es.reflect.apply.js | 以指定 this 与参数列表调用函数 |
Reflect.construct |
es.reflect.construct.js | 以参数数组调用构造函数 |
Reflect.defineProperty |
es.reflect.define-property.js | 定义属性,失败返回 false |
Reflect.deleteProperty |
es.reflect.delete-property.js | 删除属性,不可配置返回 false |
Reflect.get |
es.reflect.get.js | 读取属性值(可走原型链与 getter) |
Reflect.getOwnPropertyDescriptor |
es.reflect.get-own-property-descriptor.js | 获取自有属性描述符 |
Reflect.getPrototypeOf |
es.reflect.get-prototype-of.js | 获取原型 |
Reflect.has |
es.reflect.has.js | 等价于 in 运算符 |
Reflect.isExtensible |
es.reflect.is-extensible.js | 判断对象是否可扩展 |
Reflect.ownKeys |
es.reflect.own-keys.js | 返回自有键(含 Symbol) |
Reflect.preventExtensions |
es.reflect.prevent-extensions.js | 阻止扩展 |
Reflect.set |
es.reflect.set.js | 设置属性值,失败返回 false |
Reflect.setPrototypeOf |
es.reflect.set-prototype-of.js | 设置原型,失败返回 false |
此外,入口聚合文件 es/reflect/index.js 还会额外引入 es.reflect.to-string-tag.js 与 es.object.to-string,保证 Reflect[Symbol.toStringTag] === 'Reflect'(Object.prototype.toString.call(Reflect) 输出 [object Reflect]),这是规范对 Reflect 对象的额外要求。
三、Built-ins signatures:完整方法签名与逐项说明
原文档给出了 Reflect 命名空间的完整 TypeScript 签名,逐方法继承如下:
namespace Reflect {
apply(target: Function, thisArgument: any, argumentsList: Array<mixed>): any;
construct(target: Function, argumentsList: Array<mixed>, newTarget?: Function): Object;
defineProperty(target: Object, propertyKey: PropertyKey, attributes: PropertyDescriptor): boolean;
deleteProperty(target: Object, propertyKey: PropertyKey): boolean;
get(target: Object, propertyKey: PropertyKey, receiver?: any): any;
getOwnPropertyDescriptor(target: Object, propertyKey: PropertyKey): PropertyDescriptor | void;
getPrototypeOf(target: Object): Object | null;
has(target: Object, propertyKey: PropertyKey): boolean;
isExtensible(target: Object): boolean;
ownKeys(target: Object): Array<string | symbol>;
preventExtensions(target: Object): boolean;
set(target: Object, propertyKey: PropertyKey, V: any, receiver?: any): boolean;
setPrototypeOf(target: Object, proto: Object | null): boolean; // required __proto__ - IE11+
}
逐项说明参数与返回值约定:
Reflect.apply(target, thisArgument, argumentsList):等价于Function.prototype.apply,但argumentsList必须为类数组对象(源码中用anObject强制校验,见 es.reflect.apply.js)。Reflect.construct(target, argumentsList, newTarget?):等价于new target(...argumentsList);第三参数可改变new.target,返回新实例对象。Reflect.defineProperty/Reflect.deleteProperty/Reflect.set/Reflect.setPrototypeOf/Reflect.preventExtensions:失败不抛异常,返回false。Reflect.get与Reflect.set的receiver参数:可指定 getter/setter 执行时的this,实现代理语义。Reflect.ownKeys:返回的数组同时包含字符串键与 Symbol 键,且返回类型为Array<string | symbol>。Reflect.setPrototypeOf的注释required __proto__ - IE11+:表明在 IE11 及更新版本中要求引擎支持__proto__才能正确 polyfill 该语义(源码中该模块仅在objectSetPrototypeOf存在时才定义,见 es.reflect.set-prototype-of.js)。
四、源码级剖析:core-js 如何还原 Reflect 语义
4.1 Reflect.ownKeys:一行委托 + 内置键收集
es.reflect.own-keys.js 非常简洁,直接委托给 internals/own-keys 工具:它内部合并 Object.getOwnPropertyNames 与 Object.getOwnPropertySymbols 的结果,从而同时覆盖字符串键与 Symbol 键,这正是 Reflect.ownKeys 区别于 Object.keys(仅返回可枚举字符串键)的核心。
4.2 Reflect.apply:检测 MS Edge 的省略参数 bug
es.reflect.apply.js 用 fails 检测旧版 MS Edge 允许省略 argumentsList 参数的偏差行为(OPTIONAL_ARGUMENTS_LIST),一旦检测到就 forced 强制覆盖为规范实现:先 aCallable(target) 校验可调用性,再 anObject(argumentsList) 校验参数列表,最终调用 functionApply 完成调用。
4.3 Reflect.construct:两大引擎 bug 与零参数优化
es.reflect.construct.js 是模块中最复杂的实现,它检测并修复了两类已知问题:
NEW_TARGET_BUG(Firefox Nightly):原生实现接受第三个参数newTarget,但不会依据它创建this,导致nativeConstruct(fn, [], F) instanceof F为假。ARGS_BUG:某些引擎调用Reflect.construct(fn)(省略参数列表)会失败。
当存在上述任一 bug 时,模块被标记为 forced 且 sham,使用自定义实现。在 Target === newTarget 的常见路径下,代码对 0~4 个参数做了 new Target(...) 的展开优化;参数更多时则通过 Function.prototype.bind 拼接参数后 new。当 newTarget 被改写时,则从 newTarget.prototype 创建原型对象、以该对象为 this 调用 Target,并处理返回值是否为对象的两种情况。这解释了为什么 Reflect.construct(C, [20, 22]) 可以构造出 instance.c === 42 的实例(见下文示例)。
4.4 Reflect.get / Reflect.set:原型链递归与 receiver 语义
es.reflect.get.js 的内部函数 $get 是递归实现:先查自有属性描述符,数据描述符直接返回值,访问器描述符则以 receiver 为 this 调用 getter;若属性不存在则沿 getPrototypeOf 向上递归,直到原型为 null。当 target === receiver 时走快速路径 target[propertyKey]。
es.reflect.set.js 的 $set 则完整实现了规范写入算法:处理只读数据属性返回 false、在 receiver 上执行 defineProperty、调用原型链上的 setter,并通过 MS_EDGE_BUG 检测修复了 MS Edge 17-18 中"允许向原型只读属性所在对象写入"的错误。
4.5 Reflect.defineProperty / Reflect.deleteProperty / Reflect.has
- es.reflect.define-property.js 用
ERROR_INSTEAD_OF_FALSE检测 MS Edge 抛异常而非返回false的偏差,且会先做ToPropertyDescriptor校验(getter/setter 必须是可调用或undefined),非法描述符直接抛TypeError——这与规范"属性描述符错误应抛异常、定义失败才返回 false"的边界一致。 - es.reflect.delete-property.js 先取属性描述符,若
configurable === false返回false,否则执行delete target[key]——这正是"严格模式下delete抛错、Reflect.deleteProperty返回 false"的实现来源。 - es.reflect.has.js 整个实现就是
propertyKey in target,与in运算符等价。
4.6 其余模块与 sham 标记
getOwnPropertyDescriptor、getPrototypeOf 分别依赖 internals/descriptors 与 internals/correct-prototype-getter 的能力检测,在不支持属性描述符或原生原型 getter 的环境下标记 sham(即该实现只是近似模拟)。is-extensible、prevent-extensions、set-prototype-of 均以各自 internals 工具为基底,setPrototypeOf 在引擎不支持时直接不定义该属性。这些 forced/sham 标记正是 core-js 在 packages/core-js/modules/ 中统一采用的"先能力检测、后决定是否覆盖"的 polyfill 策略。
五、Entry points:四套入口与单方法精确引入
原文档给出了 Reflect 的完整入口路径模板(详见文档的 Entry points 章节):
core-js(-pure)/es|stable|actual|full/reflect
core-js(-pure)/es|stable|actual|full/reflect/apply
core-js(-pure)/es|stable|actual|full/reflect/construct
core-js(-pure)/es|stable|actual|full/reflect/define-property
core-js(-pure)/es|stable|actual|full/reflect/delete-property
core-js(-pure)/es|stable|actual|full/reflect/get
core-js(-pure)/es|stable|actual|full/reflect/get-own-property-descriptor
core-js(-pure)/es|stable|actual|full/reflect/get-prototype-of
core-js(-pure)/es|stable|actual|full/reflect/has
core-js(-pure)/es|stable|actual|full/reflect/is-extensible
core-js(-pure)/es|stable|actual|full/reflect/own-keys
core-js(-pure)/es|stable|actual|full/reflect/prevent-extensions
core-js(-pure)/es|stable|actual|full/reflect/set
core-js(-pure)/es|stable|actual|full/reflect/set-prototype-of
使用要点:
es/stable/actual/full四个层级分别对应"仅标准 ES 特性""稳定特性(不含提案)""已按最新语义更新的当前版本""完整(含提案特性)"。Reflect属于已定稿的 ES2015 特性,四套入口均可用,按项目对提案特性的开放程度选择即可。仓库中对应的聚合入口文件见 packages/core-js/es/reflect/index.js、packages/core-js/stable/reflect/、packages/core-js/actual/reflect/、packages/core-js/full/reflect/目录。core-js与core-js-pure:前者直接修改全局Reflect,后者通过core-js-pure包返回独立副本,不污染全局(packages/core-js-pure/override/ 存放对应的覆写模块)。- 单方法入口:如
core-js/es/reflect/own-keys只引入ownKeys一个方法,避免全量引入带来的体积开销,适合按需加载。
典型用法:
// 引入全部 Reflect 方法(写入全局)
import 'core-js/es/reflect';
// 或只引入需要的单方法
import 'core-js/es/reflect/own-keys';
import 'core-js/es/reflect/construct';
// 使用 core-js-pure,不污染全局
const { Reflect: PureReflect } = require('core-js-pure/es/reflect');
六、Examples:可运行的实战示例
原文档给出了两个核心示例,这里完整继承并补充验证:
let object = { a: 1 };
Object.defineProperty(object, 'b', { value: 2 });
object[Symbol('c')] = 3;
Reflect.ownKeys(object); // => ['a', 'b', Symbol(c)]
function C(a, b) {
this.c = a + b;
}
let instance = Reflect.construct(C, [20, 22]);
instance.c; // => 42
补充的实战场景:
// 1) Reflect.apply:以指定 this 调用,参数列表必须是类数组
Reflect.apply(String.prototype.replace, 'abc', ['b', 'x']); // => 'axc'
// 2) Reflect.set 失败时返回 false(严格模式 delete 会抛错,这里不会)
const frozen = Object.freeze({ a: 1 });
Reflect.set(frozen, 'a', 2); // => false
Reflect.deleteProperty(frozen, 'a'); // => false
// 3) 带 receiver 的 getter 调用
const target = { get x() { return this.y; } };
Reflect.get(target, 'x', { y: 42 }); // => 42
// 4) has 等价于 in
Reflect.has({ a: 1 }, 'a'); // => true
// 5) construct 的 newTarget 参数
function Parent() { this.type = 'parent'; }
function Child() {}
const obj = Reflect.construct(Parent, [], Child);
obj instanceof Parent; // => false
obj instanceof Child; // => true
这些语义在仓库的单元测试中均有覆盖,例如 tests/unit-global/es.reflect.own-keys.js、tests/unit-global/es.reflect.construct.js、tests/unit-global/es.reflect.set.js 等,读者可在 tests/unit-global/ 目录下找到全部 14 个对应测试文件,作为行为验证与回归依据。
七、小结
core-js 对 Reflect 的 polyfill 是"逐方法模块化 + 引擎 bug 检测 + 规范语义还原"三者的结合:14 个 es.reflect.*.js 模块(另加 to-string-tag 补齐标签)均可独立引入;es | stable | actual | full 与 core-js(-pure) 的组合提供了从全局注入到无污染副本的灵活加载方式;而 fails 检测驱动的 forced/sham 机制(如 Reflect.construct 的 NEW_TARGET_BUG、Reflect.apply 的 MS Edge 省略参数问题)确保了在旧引擎上也能获得与规范一致的行为。按需引入 core-js/es/reflect/xxx 单方法入口,即可在保持体积可控的同时获得可靠的反射语义支持。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust4.21 K637- DDeepSeek-V4.1-FlashDeepSeek-V4.1-Flash 是一个多模态混合专家(MoE)模型,拥有 5520 亿骨干参数,并支持最多一百万 token 的上下文长度。该模型原生支持图像和文本输入,并以自回归方式生成文本Python320
cherry-studio🍒 Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端TypeScript2 K146
hello-agents📚 《从零开始构建智能体》——从零开始的智能体原理与实践教程Python46567
new-apiAI模型聚合管理中转分发系统,一个应用管理您的所有AI模型,支持将多种大模型转为统一格式调用,支持OpenAI、Claude、Gemini等格式,可供个人或者企业内部管理与分发渠道使用。🍥 A Unified AI Model Management & Distribution System. Aggregate all your LLMs into one app and access them via an OpenAI-compatible API, with native support for Claude (Messages) and Gemini formats.Go20043
JeecgBoot🔥企业级低代码平台集成了AI应用平台,帮助企业快速实现低代码开发和构建AI应用!前后端分离架构 SpringBoot,SpringCloud、Mybatis,Ant Design4、 Vue3.0、TS+vite!强大的代码生成器让前后端代码一键生成,无需写任何代码! 引领AI低代码开发模式: AI生成->OnlineCoding-> 代码生成-> 手工MERGE,显著的提高效率,又不失灵活~Java33951