You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

直接赋值exports与Object.assign(exports)的导出差异原因解析

为什么CommonJS中Object.assign添加的属性无法被ES模块作为命名导出识别?

核心原因

Node.js在实现CommonJS(CJS)与ES模块(ESM)的兼容时,仅会通过静态扫描模块代码的方式,识别直接赋值给exports或module.exports的属性作为命名导出;而通过Object.assign、动态属性赋值(如exports['b'] = xxx)这类运行时动态添加的属性,不会被静态分析捕获,因此无法作为命名导出被ESM识别。

详细机制解析

  1. 模块系统的本质差异
    ESM是静态模块系统,依赖预解析阶段确定所有导出和导入;而CJS是动态模块系统,允许在运行时动态修改导出对象。Node.js为了让两者互通,设计了一套兼容层,但这套兼容层无法完全抹平两者的差异。

  2. CJS转ESM命名导出的静态分析逻辑
    当Node.js加载一个CJS模块供ESM导入时,会在模块执行前先做一次静态代码扫描:

    • 仅识别形如exports.xxx = ...或module.exports.xxx = ...的直接赋值语句;
    • 将这些语句中的xxx记录为该模块的命名导出项。
      这个过程不执行模块代码,所以任何运行时才会执行的动态属性添加操作,都不会被纳入命名导出的列表。
  3. 测试场景对应解释
    在你的测试代码中:

    • exports.a = 67是直接赋值语句,静态分析阶段被捕获,因此a成为可被ESM识别的命名导出;
    • Object.assign(exports, { b: 68 })是运行时执行的动态操作,静态扫描无法预知它会给exports添加b属性,所以b不会被加入命名导出列表。
      而import * as module1中看到的default导出,对应CJS模块的完整exports对象(运行时生成),所以b会出现在default里,但无法作为顶级命名导出被解构导入。

补充验证

如果将Object.assign替换为动态属性赋值exports['b'] = 68,同样无法被ESM作为命名导出识别,因为静态分析也无法识别这种动态键的赋值操作。

内容的提问来源于stack exchange,提问作者s100

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 03:33:29