ChatGPT解决这个技术问题 Extra ChatGPT

理解 Node.js 模块:多个需要返回相同的对象?

我有一个与 the node.js documentation on module caching 相关的问题:

模块在第一次加载后被缓存。这意味着(除其他外)每次调用 require('foo') 都将返回完全相同的对象,如果它会解析为同一个文件。多次调用 require('foo') 可能不会导致模块代码被多次执行。这是一个重要的特点。有了它,可以返回“部分完成”的对象,从而允许加载传递依赖项,即使它们会导致循环。

may 是什么意思?

我想知道 require 是否会总是返回相同的对象。因此,如果我在 app.js 中需要一个模块 A 并更改 app.js 中的导出对象(需要返回的对象),然后需要一个模块 Bapp.js 本身需要模块 A,我会总是获得该对象的修改版本还是新版本?

// app.js

var a = require('./a');
a.b = 2;
console.log(a.b); //2

var b = require('./b');
console.log(b.b); //2

// a.js

exports.a = 1;

// b.js

module.exports = require('./a');
文档中的那句话本来可以写得更好。在我看来,可能与不允许不同,即多次调用 require('foo') 不会导致模块代码被多次执行。
@LucioPaiva 创建了一个 PR 来修复它:github.com/nodejs/node/pull/23143
他们的意思是 [ [可能不会] 导致 ],而不是 [[ 可能] 不会导致 ],就像“不,蒂米,你可能没有更多的巧克力了”。因此,在这种情况下,我同意,这是模棱两可的。

P
Petr Stodulka

如果 app.jsb.js 都驻留在 同一个项目中(并且在同一个目录中),那么它们都将收到 A同一个实例。从 node.js documentation

...每次调用 require('foo') 都将返回完全相同的对象,如果它会解析为同一个文件。

a.jsb.jsapp.js 位于不同的 npm 模块中时,情况会有所不同。例如:

[APP] --> [A], [B]
[B]   --> [A]

在这种情况下,app.js 中的 require('a') 将解析为与 b.js 中的 require('a') 不同的 a.js 副本,因此返回 A不同实例。有一个 blog post 更详细地描述了这种行为。


“并且在同一个目录中”。当 [B] 位于 [App] 所在的子文件夹中时,我收到了相同的实例。
@BillTarbell 当我想从不同的文件夹中获取相同的实例时,我得到了两个不同的实例。
既然 npm 会将 A、B 和 APP 安装在一个平面目录结构中,最后一个警告是否仍然正确?如果没有,如何设置一个模块以在多个其他模块之间缓存其结果?
从 node.js 文档中:“模块根据其解析的文件名进行缓存。由于模块可能会根据调用模块的位置(从 node_modules 文件夹加载)解析为不同的文件名,因此不能保证 require('foo ') 将始终返回完全相同的对象,如果它会解析为不同的文件。”所以你不能使用 require 缓存来实现单例行为、不区分大小写的文件系统或符号链接等可能导致同一个模块被多次加载。如果您需要全局数据,则需要使用“全局”对象。
m
moe

node.js 实现了某种缓存,在执行一些大型服务器项目时阻止 node 读取文件 1000 次。

此缓存列在 require.cache 对象中。我必须注意,这个对象是可读/可写的,它可以从缓存中删除文件而不会终止进程。

http://nodejs.org/docs/latest/api/globals.html#require.cache

哦,忘记回答问题了。修改导出的对象不会影响下一次模块加载。这会造成很多麻烦......要求总是返回一个新的对象实例,没有引用。编辑文件并删除缓存确实会更改导出的对象

在做了一些测试之后,node.js 确实缓存了 module.exports。修改 require.cache[{module}].exports 以一个新的、修改后的返回对象结束。


我发布的代码确实有效。 b.b 用值 2 定义。所以在这个例子中它是同一个对象。
这是一个功能,在我看来非常有用。问题是我是否可以依赖它。该文档说 may 这使得它不清楚。
在 a.js 中对 require 执行函数不会改变任何东西......它会不断缓存返回的对象。奇怪,我的应用程序是如何工作的?唯一的方法是,如文档中所述,在 require 之后执行传递的函数。
有时对象是相同的。不要依赖它,如果需要,传递单个模块实例。
有没有办法在需要时取回一个全新的对象(不是缓存的对象)?
R
Reg Edit

自问题发布以来,已更新 the document 以明确说明最初使用“可能”的原因。它现在通过使事情变得明确来回答问题本身(我强调显示发生了什么变化):

模块在第一次加载后被缓存。这意味着(除其他外)每次调用 require('foo') 都将返回完全相同的对象,如果它会解析为同一个文件。如果 require.cache 没有被修改,多次调用 require('foo') 不会导致模块代码被多次执行。这是一个重要的特点。有了它,可以返回“部分完成”的对象,从而允许加载传递依赖项,即使它们会导致循环。


S
Simone C.

对于我所看到的,如果模块名称解析为先前加载的文件,则将返回缓存的模块,否则将单独加载新文件。

也就是说,缓存基于得到解析的实际文件名。这是因为,一般来说,同一包的不同版本可能安装在文件层次结构的不同级别,并且必须相应地加载。

我不确定是否存在不受程序员控制或意识的缓存失效的情况,这可能会导致意外地多次重新加载相同的包文件。


E
Evgeniy Berezovsky

如果您希望 require(x) 每次都返回一个新对象的原因仅仅是因为您直接修改了该对象 - 这是我遇到的一种情况 - 只需克隆它,然后修改并只使用克隆,如下所示:

var a = require('./a');
a = JSON.parse(JSON.stringify(a));

u
user2030657

试试 drexhttps://github.com/yuryb/drex

drex 正在监视一个模块的更新,并在更新后干净地重新需要该模块。新代码被 require()d 就好像新代码是一个完全不同的模块,所以 require.cache 不是问题。


问题是另一个问题,但 drex 看起来真的很适合开发的东西。谢谢!
A
Amir Fo

当你需要一个对象时,你需要它的引用地址,并且通过两次需要该对象,你将获得相同的地址!要拥有相同对象的副本,您应该复制(克隆)它。

var obj = require('./obj');

a = JSON.parse(JSON.stringify(obj));
b = JSON.parse(JSON.stringify(obj));
c = JSON.parse(JSON.stringify(obj));

克隆以多种方式完成,您可以查看 this,了解更多信息。