如何在NodeJS模块中共享常量?


239

目前,我正在这样做:

foo.js

const FOO = 5;

module.exports = {
    FOO: FOO
};

并在中使用它bar.js

var foo = require('foo');
foo.FOO; // 5

有一个更好的方法吗?在exports对象中声明常量感觉很尴尬。


6
如果要导出,请将其放在中exports。这有什么尴尬?
Alex Wayne

5
我习惯了C#和PHP。我想我只需要习惯两次定义每个常量。也许将来我们会export const FOO = 5;

1
@塔未来就在眼前(ES2015)!2ality.com/2014/09/…–
西班牙

1
这在功能上与更简洁的有所不同module.exports={FOO:5};吗?
Joe Lapp

3
它不仅感到笨拙,而且不再固定不变
Ini

Answers:


96

您可以使用将其显式导出到全局范围global.FOO = 5。然后,您只需要提供文件,甚至不保存您的返回值。

但实际上,您不应该这样做。保持适当封装是一件好事。您已经有了正确的想法,因此请继续做您正在做的事情。


51
我很抱歉这样做,但是-1表示了解更多,但没有提供替代(更好)的解决方案;(关于:“但实际上,您不应该这样做。保持正确封装是一件好事。”)
谢谢您

22
如果整个软件开发社区都这么想,那么我们仍然会使用穿孔机。幸运的是,有一些特立独行的人知道什么时候打破我们强加给自己的疯狂规则更好。如果封装有用,请使用它。如果是一个紧张的保姆阻止您完成工作,请解雇紧张的保姆并继续下去。
不同步的2014年

22
@naomik(超级延迟回复时间)我没有提供更好的解决方案的真正原因是因为OP已经知道解决方案。将事物封装在自己的模块中,并在必要时要求它们。
亚历克斯·韦恩

1
那么这是不是一个实际的答案,而应被解释评论,指出“你做的不够好,替代方案是坏的” ..
安德烈·波波夫

1
封装应用错误。当类使用特殊值作为指示符并为其指定名称时,您希望与使用该类的任何代码共享该特殊值。
grantwparks

314

在我看来,利用Object.freeze允许DRYer和更具声明性的风格。我的首选模式是:

./lib/constants.js

module.exports = Object.freeze({
    MY_CONSTANT: 'some value',
    ANOTHER_CONSTANT: 'another value'
});

./lib/some-module.js

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

console.log(constants.MY_CONSTANT); // 'some value'

constants.MY_CONSTANT = 'some other value';

console.log(constants.MY_CONSTANT); // 'some value'

过时的性能警告

以下问题已于2014年1月在v8中修复,不再与大多数开发人员相关:

要知道,这两种设置可写为false,并使用Object.freeze在V8中一个巨大的性能损失- https://bugs.chromium.org/p/v8/issues/detail?id=1858http://jsperf.com /性能冻结对象


4
Object.freeze的好用例!
Estus Flask

如果我需要导出常量和函数,应该是什么样?我是否也应该将功能放在冻结块中?
汤姆(Tom)

3
这种方法更好,因为可以使用IDE自动完成功能。
David A

3
这是一个很好的答案,但是由于最后关于v8性能的警告已经过时,这可能使人们放弃这种方法。请考虑删除警告。
sampathsris

4
谢谢@Krumia!我已经对其进行了更新,但是将原始警告文本留给了历史背景(并且因为其中一些注释如果没有注释就没有意义了)。
西班牙火车

163

从技术上讲 const它不是ECMAScript规范的一部分。另外,使用您注意到的“ CommonJS Module”模式,您可以更改该“ constant”的值,因为它现在只是一个对象属性。(不确定是否会将对同一脚本的其他更改进行级联,但是有可能)

要获得真正的常数,你也可以分享,看看Object.createObject.definePropertyObject.defineProperties。如果设置writable: false,则“常量”中的值无法修改。:)

这有点冗长(但即使使用一点JS也可以更改),但是您只需为常量模块执行一次即可。使用这些方法,您遗漏的任何属性都默认为false。(与通过赋值定义属性相反,后者将所有属性默认为true

因此,假设地,您可以设置valueenumerable,省去,writable并且configurable由于它们将默认设置为false,为了清楚起见,我仅将它们包括在内。

更新 - 在这个用例中,我已经创建了一个具有辅助功能的新模块(node-constants)。

constants.js-好

Object.defineProperty(exports, "PI", {
    value:        3.14,
    enumerable:   true,
    writable:     false,
    configurable: false
});

constants.js-更好

function define(name, value) {
    Object.defineProperty(exports, name, {
        value:      value,
        enumerable: true
    });
}

define("PI", 3.14);

script.js

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

console.log(constants.PI); // 3.14
constants.PI = 5;
console.log(constants.PI); // still 3.14

2
@AntoineHedgecock没必要,请查看上的文档Object.defineProperty()false在此上下文中假定所有未指定的属性。
Dominic Barnes

6
同样值得注意的是,Object.freeze()
damianb

1
这是这个问题的最好答案。+1。如果可以的话,我会更多投票。
瑞安

1
美妙的答案,非常优雅和安全的解决方案。
亚历克斯(Alex)2014年

1
@SpainTrain这似乎已由codereview.chromium.org/135903014
修复-Grinde

100

ES6方式。

在foo.js中导出

const FOO = 'bar';
module.exports = {
  FOO
}

导入bar.js

const {FOO} = require('foo');

40
是。堆栈溢出需要一种折旧过时答案的方法。
里克·乔利

7
请注意,是constin bar.js强制实施了结构化变量的不变性,而不是constin foo.js。也就是说,可以使用let {FOO} =bar.js与变异的“常数”变量。AFAIK为加强出口的不变性,仍然需要ES模块或Object.freeze
西班牙火车

一个人也可以FOO在里面改变foo.js
lima_fil

16

我发现Dominic建议的解决方案是最好的解决方案,但它仍然缺少“ const”声明的一项功能。当您使用“ const”关键字在JS中声明一个常量时,将在解析时而不是在运行时检查该常量的存在。因此,如果您稍后在代码中的某个地方拼写了常量的名称,则在尝试启动node.js程序时会收到错误消息。这是一个更好的拼写检查。

如果使用像Dominic建议的define()函数定义常量,则如果拼写错误的常量将不会出错,并且拼写错误的常量的值将是未定义的(这可能导致调试麻烦)。

但是我想这是我们能得到的最好的结果。

另外,这是对constans.js中Dominic函数的一种改进:

global.define = function ( name, value, exportsObject )
{
    if ( !exportsObject )
    {
        if ( exports.exportsObject )
            exportsObject = exports.exportsObject;
        else 
            exportsObject = exports;        
    }

    Object.defineProperty( exportsObject, name, {
        'value': value,
        'enumerable': true,
        'writable': false,
    });
}

exports.exportObject = null;

这样,您可以在其他模块中使用define()函数,它允许您在constants.js模块内部以及从中调用该函数的模块内部定义常量。然后可以通过两种方式(在script.js中)声明模块常量。

第一:

require( './constants.js' );

define( 'SOME_LOCAL_CONSTANT', "const value 1", this ); // constant in script.js
define( 'SOME_OTHER_LOCAL_CONSTANT', "const value 2", this ); // constant in script.js

define( 'CONSTANT_IN_CONSTANTS_MODULE', "const value x" ); // this is a constant in constants.js module

第二:

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

// More convenient for setting a lot of constants inside the module
constants.exportsObject = this;
define( 'SOME_CONSTANT', "const value 1" ); // constant in script.js
define( 'SOME_OTHER_CONSTANT', "const value 2" ); // constant in script.js

另外,如果只希望从常量模块调用define()函数(而不是膨胀全局对象),则可以在constants.js中这样定义它:

exports.define = function ( name, value, exportsObject )

并在script.js中像这样使用它:

constants.define( 'SOME_CONSTANT', "const value 1" );

11

根据以前的项目经验,这是一个好方法:

在constants.js中:

// constants.js

'use strict';

let constants = {
    key1: "value1",
    key2: "value2",
    key3: {
        subkey1: "subvalue1",
        subkey2: "subvalue2"
    }
};

module.exports =
        Object.freeze(constants); // freeze prevents changes by users

在main.js(或app.js等)中,如下使用它:

// main.js

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

console.log(constants.key1);

console.dir(constants.key3);

8

我认为这const为大多数寻求解决此问题的人解决了问题。如果您确实需要一个不变的常数,请查看其他答案。为了使所有内容井井有条,我将所有常量保存在一个文件夹中,然后需要整个文件夹。

src / main.js文件

const constants = require("./consts_folder");

src / consts_folder / index.js

const deal = require("./deal.js")
const note = require("./note.js")


module.exports = {
  deal,
  note
}

附言 在这里,dealand note将是main.js的第一层

src / consts_folder / note.js

exports.obj = {
  type: "object",
  description: "I'm a note object"
}

附言 obj将成为main.js的第二层

src / consts_folder / deal.js

exports.str = "I'm a deal string"

附言 str将成为main.js的第二层

main.js文件的最终结果:

console.log(constants.deal); 输出:

{deal:{str:'I \'ma deal string'},

console.log(constants.note); 输出:

注意:{obj:{类型:“对象”,描述:“ I \'ma note对象”}}



4

作为替代方案,您可以将“常量”值分组在本地对象中,并导出返回此对象的浅表克隆的函数。

var constants = { FOO: "foo" }

module.exports = function() {
  return Object.assign({}, constants)
}

那么,是否有人重新分配FOO无关紧要,因为这只会影响其本地副本。


或者只是module.exports =()=>({FOO:“ foo”,BAR:“ bar”});
比约恩格兰博

3

由于Node.js使用的是CommonJS模式,因此只能module.exports像在浏览器中那样使用或通过设置全局变量来在模块之间共享变量,而不能使用窗口global.your_var = value;


2

最后,我通过导出具有匿名getter函数而不是常量本身的冻结对象来完成此操作。这减少了由于const名称的简单错字而引入的令人讨厌的bug的风险,因为如果遇到错字,将抛出运行时错误。这是一个完整的示例,该示例还将ES6符号用于常量,以确保唯一性以及ES6箭头功能。如果采用这种方法遇到的任何问题,我们将不胜感激。

'use strict';
const DIRECTORY = Symbol('the directory of all sheets');
const SHEET = Symbol('an individual sheet');
const COMPOSER = Symbol('the sheet composer');

module.exports = Object.freeze({
  getDirectory: () => DIRECTORY,
  getSheet: () => SHEET,
  getComposer: () => COMPOSER
});

0

我建议使用webpack进行此操作(假设您正在使用webpack)。

定义常量就像设置webpack配置文件一样简单:

var webpack = require('webpack');
module.exports = {
    plugins: [
        new webpack.DefinePlugin({
            'APP_ENV': '"dev"',
            'process.env': {
                'NODE_ENV': '"development"'
            }
        })
    ],    
};

这样,您就可以在源之外定义它们,并且它们将在所有文件中可用。


0

我认为从模块中入侵GLOBAL空间不是一个好习惯,但是在可能严格实施它的情况下:

Object.defineProperty(global,'MYCONSTANT',{value:'foo',writable:false,configurable:false});

必须考虑该资源的影响。如果没有正确命名这些常量,那么覆盖已定义的全局变量的风险是真实存在的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.