Object.assignmodule.exports,{...}与module.exports = {...}

问题描述

有人可以举例说明module.exports = {...}将如何导致意外行为。

我正在读你还不懂js,我在 https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/scope-closures/ch8.md#node-commonjs-modules

一些开发人员习惯于替换认导出 对象,例如:

// defining a new object for the API
module.exports = {
    // ..exports..
};

这种方法有一些奇怪之处,包括意外情况 如果多个这样的模块循环依赖,则会出现这种情况。如 这样,我建议不要更换对象。如果要分配 使用对象文字样式定义一次导出多个 可以改为:

Object.assign(module.exports,{
   // .. exports ..
});

这里发生的是用您的{ 指定模块的公共API,然后将Object.assign(..) 在现有属性上执行所有这些属性的浅表复制 module.exports对象,而不是替换它。这是一个不错的平衡 方便性和更安全的模块行为。

解决方法

在模块运行之前为您的模块创建exports对象,并且如果存在循环依赖关系,则其他模块可能可以访问该默认对象,然后才能将其填充。如果您替换,它们可能具有旧的原始对象,并且(最终)看不到您的导出。如果您将其添加到,则即使该对象最初没有导出,但即使其他模块在这些导出存在之前就可以访问该对象,最终也将具有该导出。

CJS模块文档的Cycles section中的更多内容。

我们可以修改该部分中的循环示例以进行演示:

a.js(注释更改):

console.log('a starting');
// Removed: `exports.done = false;`
const b = require('./b.js');
console.log('in a,b.done = %j',b.done);
exports = {done: true}; // ** Modified this line
console.log('a done');

b.js(不变):

console.log('b starting');
exports.done = false;
const a = require('./a.js');
console.log('in b,a.done = %j',a.done);
exports.done = true;
console.log('b done');

main.js(不变):

console.log('main starting');
const a = require('./a.js');
const b = require('./b.js');
console.log('in main,a.done = %j,a.done,b.done);

运行时:

main starting
a starting
b starting
in b,a.done = undefined
b done
in a,b.done = true
a done
in main,a.done = undefined,b.done = true
(node:2025) Warning: Accessing non-existent property 'done' of module exports inside circular dependency
(Use `node --trace-warnings ...` to show where the warning was created)

侧面说明:使用JavaScript自己的模块系统(ESM)对循环的处理方式不同,并且由于没有exports等效对象(您可以访问;从概念上讲),因此不会出现此问题。我建议尽可能使用ESM。自v12以来,Node.js就以相当稳定(尽管仍在不断发展)的方式对其进行了支持。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...