Node.js 和模块需要。对象不是按引用分配的?
Node.js and Modules require. Objects are not assigned by reference?
我在苦苦挣扎了 2 天之后才写下这个问题,在此期间我找不到解决方案,但也找不到解释为什么这段代码不起作用的原因。
我将报告我的代码的超级简化模拟。
我有 5 个 Node.js 个文件:
1) server.js -> Is the main file used to start the server
2) globalVars.js -> Is where all the server "global" objects are stored.
3) globalFunctions.js -> Is where all "general" functions are stored to be used by all other modules
4) helloManager.js -> Example file which contains some standard function used by the server
5) aspect.js -> Same as helloManager.js
//server.js
//Loading the globalVars file. All objects are passed by reference so I use this to store the global variables
var globalVars = require("./globalVars.js");
//Assigning to the respective global object all the functions exported from other modules
globalVars.Modules.globalFunctions = require("./globalFunctions.js");
globalVars.Modules.aspect = require("./aspect.js");
globalVars.Modules.helloManager = require("./helloManager.js");
//After this point, the objects in globalVars.js will be populated with the respective functions exported from the files
//A timeout just to be sure it is not a problem of timing? (Well, it is not...)
setTimeout(function(){
console.log(globalVars.Modules.helloManager.helloOutput());
}, 2000);
/*
Console will throw the following error:
../globalFunctions.js:6
return "MR. " + aspect.getAspect();
^
TypeError: aspect.getAspect is not a function
*/
//globalVars.js
//Objects that will be populated with the functions inside other modules
module.exports.Modules = {
aspect: {},
helloManager: {},
globalFunctions: {}
};
//helloManager.js
var globalVars = require("./globalVars.js");
var { globalFunctions } = globalVars.Modules;
module.exports.helloOutput = function(){
return "hello " + globalFunctions.getHumanData();
};
//aspect.js
module.exports.getAspect = function(){
return "human";
};
//globalFunctions.js
var globalVars = require("./globalVars.js");
var { aspect } = globalVars.Modules;
module.exports.getHumanData = function(){
return "MR. " + aspect.getAspect();
};
请不要回答我将所有内容都放在同一个文件中,因为我的代码报告起来要复杂得多,所以我在这里发布这个非常简单的模拟。
我知道对象是通过引用分配的,所以如果所有模块都从“globalVars”获取变量,它们的工作方式有点像“global”。
问题是在 globalFunctions.js 我加载
var { aspect } = globalVars.Modules;
因为在server.js中模块aspect.js还没有被加载,所以它将是一个空对象.
但我期待
var { aspect } = globalVars.Modules;
正在获取 globalVars 的引用而不是副本,因此当 server.js 完成加载所有模块时, globalVars.Modules 中的变量 aspect 将指向正确的对象,因此它会找到我需要的函数!
事实上,server.js 中的 console.log 是在加载所有模块后执行的,正是出于这个原因。
有谁知道这个问题的原因是什么,我该如何解决?
感谢所有愿意提供帮助的人!
如您所见,var { aspect } = globalVars.Modules;
将 globalVars.Modules.aspect
的 当前 值复制到局部变量 aspect
。它只是 var aspect = globalVars.Modules.aspect
.
的替代语法
如果您稍后将 globalVars.Modules.aspect
的值更改为新对象(而不是改变已经存在的对象),则局部变量不会更新。
如果您想要最新的值,则需要在需要时继续访问 globalVars.Modules.aspect
。
发生了什么事
这是做const { aspect } = globalVars.Modules;
(与const aspect = globalVars.Modules.aspect;
相同)意味着什么的问题。也就是说,这是赋值语义的问题。
让我们看一个更简单的例子,然后我们可以看到它适用于你正在做的事情。假设您有:
let a = {/*original object*/};
当你这样做时
b = a;
将 a
中的 值 复制到 b
中。该值是一个对象引用,因此它们现在都指向同一个对象,但是 a
和 b
之间没有进行中的 link。如果你这样做
a = {/*new object*/};
这对 b
没有任何影响,因为 a
(变量)和 b
(变量)之间没有正在进行的 link。 b
仍然引用原始对象,而不是新对象。
这同样适用于任何可分配的项目,例如对象 属性 或参数。 globalVars.Modules.aspect
就是这样。 globalFunctions
正在获取值(简单的赋值语义,尽管使用了解构),然后 server.js
正在用一个新值替换该值。
这是您的代码中发生的情况:
// server.js
var globalVars = (function() {
// globalVars.js
return { // this is the `exports` object
Modules: {
aspect: {}, // *** That's the `a = {/*original object*/}`
}
};
})();
// back in server.js
globalVars.Modules.globalFunctions = (function() {
// globalFunctions.js
const { aspect } = globalVars.Modules; // **** That's the `b = a`
return { // this is the `exports` object
getHumanData: function(){
return "MR. " + aspect.getAspect();
}
};
})();
// back in server.js
globalVars.Modules.aspect = (function() { // *** that's the `a = {/*new object*/}
return { // this is the `exports` object
getAspect: function(){
return "human";
}
};
})();
// back in server.js
globalVars.Modules.globalFunctions.getHumanData(); // Fails because the object it's using
// is the old one, not the new one
如何修复
globalFunctions.js
依赖aspect.js
,直接依赖:
// In `globalFunctions.js`
const aspect = require("./aspect.js");
module.exports.getHumanData = function(){
return "MR. " + aspect.getAspect();
};
假设没有循环,那行得通。
在更大的层面上:可能根本没有理由拥有 globalVars.Modules
。一个模块只加载一次(通常),所以而不是 globalVars.Modules
,让每个模块直接依赖它需要的模块,而不是通过一个中心对象将它们全部集中起来。 Node.js' 模块缓存已经 中心对象。
如果你不想globalFunctions.js
直接依赖aspect.js
(为什么不呢?),那么不要从[=复制aspect
属性 36=], 需要时使用:
// In `globalFunctions.js`
const {Modules} = require("./globalVars.js");
module.exports.getHumanData = function(){
return "MR. " + Modules.aspect.getAspect();
};
假设没有任何重新分配 Modules
(在您显示的代码中似乎没有任何重新分配),这将起作用。但同样,如果可以的话,直接依赖 aspect.js
更有意义。
有趣的是,这是现代 ESM 模块不像 CommonJS 那样使用简单赋值语义的原因之一。 ESM 不会帮助你的具体事情,因为你使用自己的 globalVars.Modules
对象而不是使用模块对象,但它解决了人们经常遇到的 CommonJS 模块问题,就像你的问题一样,是由期望 b
(导入值)在重新分配 a
(导出值)时受到影响。人们对 CommonJS 的问题主要发生在两个模块之间存在循环时(循环依赖,直接或间接)。 ESM 通过使导入绑定(在我的示例中为 b
)成为导出绑定(在我的示例中为 a
)的 实时绑定 来解决此问题。这是 JavaScript 唯一一个你可以争论的地方是 pass-by-reference 的一种形式(对变量的引用)。
我在苦苦挣扎了 2 天之后才写下这个问题,在此期间我找不到解决方案,但也找不到解释为什么这段代码不起作用的原因。
我将报告我的代码的超级简化模拟。
我有 5 个 Node.js 个文件:
1) server.js -> Is the main file used to start the server
2) globalVars.js -> Is where all the server "global" objects are stored.
3) globalFunctions.js -> Is where all "general" functions are stored to be used by all other modules
4) helloManager.js -> Example file which contains some standard function used by the server
5) aspect.js -> Same as helloManager.js
//server.js
//Loading the globalVars file. All objects are passed by reference so I use this to store the global variables
var globalVars = require("./globalVars.js");
//Assigning to the respective global object all the functions exported from other modules
globalVars.Modules.globalFunctions = require("./globalFunctions.js");
globalVars.Modules.aspect = require("./aspect.js");
globalVars.Modules.helloManager = require("./helloManager.js");
//After this point, the objects in globalVars.js will be populated with the respective functions exported from the files
//A timeout just to be sure it is not a problem of timing? (Well, it is not...)
setTimeout(function(){
console.log(globalVars.Modules.helloManager.helloOutput());
}, 2000);
/*
Console will throw the following error:
../globalFunctions.js:6
return "MR. " + aspect.getAspect();
^
TypeError: aspect.getAspect is not a function
*/
//globalVars.js
//Objects that will be populated with the functions inside other modules
module.exports.Modules = {
aspect: {},
helloManager: {},
globalFunctions: {}
};
//helloManager.js
var globalVars = require("./globalVars.js");
var { globalFunctions } = globalVars.Modules;
module.exports.helloOutput = function(){
return "hello " + globalFunctions.getHumanData();
};
//aspect.js
module.exports.getAspect = function(){
return "human";
};
//globalFunctions.js
var globalVars = require("./globalVars.js");
var { aspect } = globalVars.Modules;
module.exports.getHumanData = function(){
return "MR. " + aspect.getAspect();
};
请不要回答我将所有内容都放在同一个文件中,因为我的代码报告起来要复杂得多,所以我在这里发布这个非常简单的模拟。
我知道对象是通过引用分配的,所以如果所有模块都从“globalVars”获取变量,它们的工作方式有点像“global”。
问题是在 globalFunctions.js 我加载
var { aspect } = globalVars.Modules;
因为在server.js中模块aspect.js还没有被加载,所以它将是一个空对象. 但我期待
var { aspect } = globalVars.Modules;
正在获取 globalVars 的引用而不是副本,因此当 server.js 完成加载所有模块时, globalVars.Modules 中的变量 aspect 将指向正确的对象,因此它会找到我需要的函数!
事实上,server.js 中的 console.log 是在加载所有模块后执行的,正是出于这个原因。
有谁知道这个问题的原因是什么,我该如何解决? 感谢所有愿意提供帮助的人!
如您所见,var { aspect } = globalVars.Modules;
将 globalVars.Modules.aspect
的 当前 值复制到局部变量 aspect
。它只是 var aspect = globalVars.Modules.aspect
.
如果您稍后将 globalVars.Modules.aspect
的值更改为新对象(而不是改变已经存在的对象),则局部变量不会更新。
如果您想要最新的值,则需要在需要时继续访问 globalVars.Modules.aspect
。
发生了什么事
这是做const { aspect } = globalVars.Modules;
(与const aspect = globalVars.Modules.aspect;
相同)意味着什么的问题。也就是说,这是赋值语义的问题。
让我们看一个更简单的例子,然后我们可以看到它适用于你正在做的事情。假设您有:
let a = {/*original object*/};
当你这样做时
b = a;
将 a
中的 值 复制到 b
中。该值是一个对象引用,因此它们现在都指向同一个对象,但是 a
和 b
之间没有进行中的 link。如果你这样做
a = {/*new object*/};
这对 b
没有任何影响,因为 a
(变量)和 b
(变量)之间没有正在进行的 link。 b
仍然引用原始对象,而不是新对象。
这同样适用于任何可分配的项目,例如对象 属性 或参数。 globalVars.Modules.aspect
就是这样。 globalFunctions
正在获取值(简单的赋值语义,尽管使用了解构),然后 server.js
正在用一个新值替换该值。
这是您的代码中发生的情况:
// server.js
var globalVars = (function() {
// globalVars.js
return { // this is the `exports` object
Modules: {
aspect: {}, // *** That's the `a = {/*original object*/}`
}
};
})();
// back in server.js
globalVars.Modules.globalFunctions = (function() {
// globalFunctions.js
const { aspect } = globalVars.Modules; // **** That's the `b = a`
return { // this is the `exports` object
getHumanData: function(){
return "MR. " + aspect.getAspect();
}
};
})();
// back in server.js
globalVars.Modules.aspect = (function() { // *** that's the `a = {/*new object*/}
return { // this is the `exports` object
getAspect: function(){
return "human";
}
};
})();
// back in server.js
globalVars.Modules.globalFunctions.getHumanData(); // Fails because the object it's using
// is the old one, not the new one
如何修复
globalFunctions.js
依赖aspect.js
,直接依赖:
// In `globalFunctions.js`
const aspect = require("./aspect.js");
module.exports.getHumanData = function(){
return "MR. " + aspect.getAspect();
};
假设没有循环,那行得通。
在更大的层面上:可能根本没有理由拥有 globalVars.Modules
。一个模块只加载一次(通常),所以而不是 globalVars.Modules
,让每个模块直接依赖它需要的模块,而不是通过一个中心对象将它们全部集中起来。 Node.js' 模块缓存已经 中心对象。
如果你不想globalFunctions.js
直接依赖aspect.js
(为什么不呢?),那么不要从[=复制aspect
属性 36=], 需要时使用:
// In `globalFunctions.js`
const {Modules} = require("./globalVars.js");
module.exports.getHumanData = function(){
return "MR. " + Modules.aspect.getAspect();
};
假设没有任何重新分配 Modules
(在您显示的代码中似乎没有任何重新分配),这将起作用。但同样,如果可以的话,直接依赖 aspect.js
更有意义。
有趣的是,这是现代 ESM 模块不像 CommonJS 那样使用简单赋值语义的原因之一。 ESM 不会帮助你的具体事情,因为你使用自己的 globalVars.Modules
对象而不是使用模块对象,但它解决了人们经常遇到的 CommonJS 模块问题,就像你的问题一样,是由期望 b
(导入值)在重新分配 a
(导出值)时受到影响。人们对 CommonJS 的问题主要发生在两个模块之间存在循环时(循环依赖,直接或间接)。 ESM 通过使导入绑定(在我的示例中为 b
)成为导出绑定(在我的示例中为 a
)的 实时绑定 来解决此问题。这是 JavaScript 唯一一个你可以争论的地方是 pass-by-reference 的一种形式(对变量的引用)。