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

目前我正在做这个:

foo.js

const FOO = 5;


module.exports = {
FOO: FOO
};

并在bar.js中使用它:

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

还有更好的办法吗?在exports对象中声明常量会让人感到尴尬。

289337 次浏览

你可以用global.FOO = 5显式地将它导出到全局作用域。然后,您只需请求该文件,甚至不保存返回值。

但说真的,你不应该这么做。保持适当的封装是一件好事。你已经有了正确的想法,所以继续做你正在做的事情。

由于Node.js正在使用CommonJS模式,你只能通过module.exports或像在浏览器中一样设置全局变量在模块之间共享变量,但你使用global.your_var = value;而不是使用window。

从技术上讲,const不是ECMAScript规范的一部分。此外,使用你已经注意到的“CommonJS Module”模式,你可以改变这个“常量”的值,因为它现在只是一个对象属性。(不确定这是否会级联需要相同模块的其他脚本的任何更改,但这是可能的)

要获得一个你也可以分享的真实常数,请查看Object.createObject.definePropertyObject.defineProperties。如果你设置了writable: false,那么你的“常量”中的值不能被修改。:)

这有点啰嗦(但即使是这样也可以用一个小JS来改变),但你应该只需要为你的常量模块做一次。使用这些方法,你省略的任何属性默认为false。(与通过赋值定义属性相反,赋值将所有属性默认为true)

所以,假设,你可以只设置valueenumerable,省略writableconfigurable,因为它们默认为false,我只是为了清晰起见才包含它们。

更新 -我已经为这个用例创建了一个带有helper函数的新模块(node-constants)。

js——很好

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

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

我发现Dominic建议的解决方案是最好的,但它仍然缺少“const”声明的一个特性。当你在JS中使用"const"关键字声明一个常量时,常量的存在是在解析时检查的,而不是在运行时。因此,如果你在后面的代码中拼写错了常量的名字,当你尝试启动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模块调用(而不是膨胀全局对象),你可以在constants.js中这样定义它:

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

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

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

在我看来,使用Object.freeze允许一个烘干机和更多的声明性风格。我喜欢的模式是:

./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版本中修复,现在已不再与大多数开发者相关:

请注意,将writable设置为false和使用Object.freeze在v8 - https://bugs.chromium.org/p/v8/issues/detail?id=1858https://jsben.ch/AhAVa中都有大量的性能损失

最后,我导出了一个带有匿名getter函数的冻结对象,而不是导出常量本身。这降低了由于简单的const名称输入错误而引入的严重错误的风险,因为在输入错误的情况下将抛出运行时错误。下面是一个完整的例子,它也使用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
});

我建议使用webpack(假设你正在使用webpack)。

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

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

通过这种方式,您可以在源代码之外定义它们,并且它们将在您的所有文件中可用。

作为一种替代方法,您可以将“常量”值分组在一个局部对象中,并导出一个返回该对象的浅克隆的函数。

var constants = { FOO: "foo" }


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

然后,如果有人重新分配FOO就无关紧要了,因为它只会影响他们的本地副本。

ES6方式。

在foo.js中导出

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

导入到bar.js中

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

从之前的项目经验来看,这是一个很好的方法:

在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);

我不认为从模块中入侵GLOBAL空间是一个好的实践,但在某些情况下,可能严格需要实现它:

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

必须考虑到这种资源的影响。如果没有正确命名这些常量,重写已经定义的全局变量的风险是真实存在的。

importexport(在2018年之前,可能需要像babel这样的东西才能使用import)

types.js

export const BLUE = 'BLUE'
export const RED = 'RED'

myApp.js

import * as types from './types.js'


const MyApp = () => {
let colour = types.RED
}

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import

我认为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
}

附注:这里的dealnote将是main.js中的第一级

src / consts_folder / note.js

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

Ps. obj将是main.js中的第二级

src / consts_folder / deal.js

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

Ps. str将是main.js中的第二级

main.js文件的最终结果:

< p > console.log(constants.deal); 输出:< / p >

{交易:{str: '我是一个交易字符串'},

< p > console.log(constants.note); 输出:< / p >

备注:{obj: {type: 'object', description: 'I\'m a note object'}}