双封装危害


【Dual package hazard】

当一个应用使用同时提供 CommonJS 和 ES 模块来源的包时,如果两个版本的包都被加载,就存在某些错误的风险。这种潜在问题源于通过 const pkgInstance = require('pkg') 创建的 pkgInstance 与通过 import pkgInstance from 'pkg'(或类似的主路径如 'pkg/module')创建的 pkgInstance 不同。这就是所谓的“混合包风险”,即同一个运行环境中可能加载两个版本的相同包。虽然应用或包不太可能故意直接加载这两个版本,但应用加载一个版本而其依赖加载另一个版本的情况很常见。由于 Node.js 支持混合使用 CommonJS 和 ES 模块,这种风险可能导致意外行为。

【When an application is using a package that provides both CommonJS and ES module sources, there is a risk of certain bugs if both versions of the package get loaded. This potential comes from the fact that the pkgInstance created by const pkgInstance = require('pkg') is not the same as the pkgInstance created by import pkgInstance from 'pkg' (or an alternative main path like 'pkg/module'). This is the “dual package hazard,” where two versions of the same package can be loaded within the same runtime environment. While it is unlikely that an application or package would intentionally load both versions directly, it is common for an application to load one version while a dependency of the application loads the other version. This hazard can happen because Node.js supports intermixing CommonJS and ES modules, and can lead to unexpected behavior.】

如果包 main 的导出是一个构造函数,通过两个版本创建的实例进行 instanceof 比较会返回 false,如果导出的是一个对象,则在一个对象上添加的属性(如 pkgInstance.foo = 3)在另一个对象上不会存在。这与在全 CommonJS 或全 ES 模块环境中 importrequire 语句的工作方式不同,因此会让用户感到意外。这也不同于用户在使用像 Babelesm 这样的工具进行转译时所熟悉的行为。

【If the package main export is a constructor, an instanceof comparison of instances created by the two versions returns false, and if the export is an object, properties added to one (like pkgInstance.foo = 3) are not present on the other. This differs from how import and require statements work in all-CommonJS or all-ES module environments, respectively, and therefore is surprising to users. It also differs from the behavior users are familiar with when using transpilation via tools like Babel or esm.】