在Node.js中如何在文件之间共享变量?

177

这里有两个文件:

// main.js
require('./module');
console.log(name); // prints "foobar"

// module.js
name = "foobar";

当我没有使用"var"时它可以工作,但当我使用了:

// module.js
var name = "foobar";

在main.js中,name将是未定义的。

我听说全局变量很糟糕,最好在引用之前使用"var"。但是这种情况下全局变量是否有利呢?


1
这个回答解决了你的问题吗?如何在Node.js中从一个文件获取变量到另一个文件中 - UrbanConor
8个回答

233

全局变量几乎永远不是一个好东西(也许有一两个例外...)。在这种情况下,看起来你只是想导出你的“name”变量。例如,

// module.js
var name = "foobar";
// export it
exports.name = name;

然后,在 main.js 中...

//main.js
// get a reference to your required module
var myModule = require('./module');

// name is a member of myModule due to the export above
var name = myModule.name;

6
全局变量不好,我完全同意这一点。但是,可能存在依赖于变量的模块。是否有一种方法可以通过 require 函数将此变量传递给其他 JS 文件呢? - appsthatmatter
2
@jjoe64 不确定我理解你的意思。你可以通过 exports 对象有效地共享任何想要的值。 - jmar777
8
OP想知道是否可以在main.js中定义变量,然后在module.js中使用。我也有同样的需求,即定义反复使用的路径。 - designermonkey
6
在这种情况下,你最好拥有一个配置对象,其中包含那些类型的值,并且可以被require()到给定的文件中。请注意,你可以只使用global.foo = 'bar',然后在任何需要的地方访问foo...但正如我在原始答案中所说的那样,那几乎从来不是一个好主意。 - jmar777
1
十年前的事了,我现在已经不记得了。我猜可能就像上面的答案一样。 - designermonkey
显示剩余2条评论

65

我找不到适用于全局 var 的最佳方案,当然你可以使用它,但看看这些示例,你可能会找到更好的解决方式:

方案1:将内容放入配置文件中

您需要一些值,在整个应用程序中都是相同的,但是根据环境(生产、开发或测试)而变化,例如邮件发送类型,您需要:

// File: config/environments/production.json
{
    "mailerType": "SMTP",
    "mailerConfig": {
      "service": "Gmail",
      ....
}

and
// File: config/environments/test.json
{
    "mailerType": "Stub",
    "mailerConfig": {
      "error": false
    }
}

也要为开发环境创建一个类似的配置文件。

为了决定加载哪个配置文件,请创建一个主配置文件(在整个应用程序中都将使用此文件)。

// File: config/config.js
var _ = require('underscore');

module.exports = _.extend(
    require(__dirname + '/../config/environments/' + process.env.NODE_ENV + '.json') || {});

现在您可以像这样获取数据:

// File: server.js
...
var config = require('./config/config');
...
mailer.setTransport(nodemailer.createTransport(config.mailerType, config.mailerConfig));

场景二:使用常量文件

// File: constants.js
module.exports = {
  appName: 'My neat app',
  currentAPIVersion: 3
};

并按照以下方式使用

// File: config/routes.js

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

module.exports = function(app, passport, auth) {
  var apiroot = '/api/v' + constants.currentAPIVersion;
...
  app.post(apiroot + '/users', users.create);
...

场景3:使用辅助函数来获取/设置数据

虽然我不是很喜欢这种方法,但至少你可以跟踪使用“名称”(引用OP的示例)并放置验证。

// File: helpers/nameHelper.js

var _name = 'I shall not be null'

exports.getName = function() {
  return _name;
};

exports.setName = function(name) {
  //validate the name...
  _name = name;
};

并使用它

// File: controllers/users.js

var nameHelper = require('../helpers/nameHelper.js');

exports.create = function(req, res, next) {
  var user = new User();
  user.name = req.body.name || nameHelper.getName();
  ...

可能存在某些情况下没有其他解决方案,除了使用全局 var,但通常情况下,您可以使用以下其中一种场景共享应用程序中的数据,如果您开始使用 node.js(就像我曾经做过),请尝试组织处理数据的方式,因为它可能会变得非常混乱。


我喜欢方案2,但是我们在构建之后是否可以更改这些值?就像我们通常做的那样,运行npm run build。或者你知道有什么方法可以在构建后更改值吗? - Kashif Ullah
@KashifUllah 不确定我是否能够仅凭提供的信息回答您的评论,您可能需要在该网站上添加一个新问题。 - Felipe Pereira

30

如果我们需要共享多个变量,请使用以下格式

//module.js
   let name='foobar';
   let city='xyz';
   let company='companyName';

   module.exports={
    name,
    city,
    company
  }

使用方法

  // main.js
    require('./modules');
    console.log(name); // print 'foobar'

2
只是一个快速的提示,以避免可能出现的混淆:应该使用module.exports!无论你的js文件叫什么(例如:global.js)。module是一个存在于全局范围内的node对象![因此在global.js中我们使用module.exports = .....] - Mohamed Allal
1
如果您删除“let”,并且不需要“module.exports..”,则会成功。 - Ahmad Zahabi
有没有一种方法可以从 module.js 中更新所有导入它的文件中的值(例如名称、城市、公司),以便更新后的值可供导入它的所有文件访问?是否有类似于“动态全局变量”的东西,可以被任何引用它的模块访问和编辑?我在这里有一个更多上下文的问题 - user1063287

8

将想要共享的任何变量保存为一个对象。然后将其传递给加载的模块,以便通过对象引用访问变量。

// main.js
var myModule = require('./module.js');
var shares = {value:123};

// Initialize module and pass the shareable object
myModule.init(shares);

// The value was changed from init2 on the other file
console.log(shares.value); // 789

在另一个文件中...
// module.js
var shared = null;

function init2(){
    console.log(shared.value); // 123
    shared.value = 789;
}

module.exports = {
    init:function(obj){
        // Save the shared object on current module
        shared = obj;

        // Call something outside
        init2();
    }
}

3

这并不是一种新方法,只是稍作优化。创建一个包含全局变量的文件,并通过 exportrequire 共享它们。在此示例中,Getter 和 Setter 更加动态,全局变量可以是只读的。要定义更多的全局变量,只需将它们添加到 globals 对象中即可。

global.js

const globals = {
  myGlobal: {
    value: 'can be anytype: String, Array, Object, ...'
  },
  aReadonlyGlobal: {
    value: 'this value is readonly',
    protected: true
  },
  dbConnection: {
    value: 'mongoClient.db("database")'
  },
  myHelperFunction: {
    value: function() { console.log('do help') }
  },
}

exports.get = function(global) {
  // return variable or false if not exists
  return globals[global] && globals[global].value ? globals[global].value : false;
};

exports.set = function(global, value) {
  // exists and is protected: return false
  if (globals[global] && globals[global].protected && globals[global].protected === true)
    return false;
  // set global and return true
  globals[global] = { value: value };
  return true;
};

在任何其他文件中获取和设置示例.js

const globals = require('./globals');

console.log(globals.get('myGlobal'));
// output: can be anytype: String, Array, Object, ...

globals.get('myHelperFunction')();
// output: do help

let myHelperFunction = globals.get('myHelperFunction');
myHelperFunction();
// output: do help

console.log(globals.set('myGlobal', 'my new value'));
// output: true
console.log(globals.get('myGlobal'));
// output: my new value

console.log(globals.set('aReadonlyGlobal', 'this shall not work'));
// output: false
console.log(globals.get('aReadonlyGlobal'));
// output: this value is readonly

console.log(globals.get('notExistingGlobal'));
// output: false

3

使用或不使用 var 关键字声明的变量将附加到全局对象。通过声明没有 var 关键字的变量来创建 Node 中的全局变量。而使用 var 关键字声明的变量仅限于模块内部。

请参阅此文章以进一步了解 - https://www.hacksparrow.com/global-variables-in-node-js.html


3
这些片段是否相互矛盾?1)“使用或不使用var关键字声明的变量会附加到全局对象”,2)“使用var关键字声明的变量仅在模块中局部存在”。 - BaldEagle

1

我持不同意见,如果您要发布代码到npm上,全局变量可能是最好的选择,因为您无法确定所有包都使用您代码的相同版本。因此,如果您使用一个文件来导出singleton对象,这将会在这里引起问题。

您可以选择globalrequire.main或其他在文件之间共享的对象。

否则,将您的包安装为可选依赖项包可以避免此问题。

请告诉我是否有更好的解决方案。


0

如果目标是浏览器(通过Parcel.js或类似方式捆绑Node代码),您可以简单地在window对象上设置属性,它们将成为全局变量:

window.variableToMakeGlobal = value;

然后你可以从所有模块(更一般地说,从任何Javascript上下文)访问这个变量。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接