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

目前我正在这样做:

foo.js

const FOO = 5; module.exports = { FOO: FOO }; 

并在bar.js使用它:

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

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

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

但是,真的,你不应该这样做。 妥善封装是件好事。 你有正确的想法,所以继续做你在做什么。

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

为了得到一个真正的常量,你也可以分享,检查出Object.createObject.definePropertyObject.defineProperties 。 如果你设置writable: false ,那么你的“常数”中的值是不能修改的。 🙂

这有点冗长,(但即使这样可以改变一个小的JS),但你只需要为你的常量模块做一次。 使用这些方法,任何你离开的属性默认为false 。 (而不是通过赋值来定义属性,这将默认所有的属性为true

所以,假设你可以设置valueenumerable value ,因为它们默认为false ,所以enumerable writableconfigurable ,为了清晰起见,我只是将它们包含在内。

更新 – 我已经创build了一个新的模块( 节点常数 )与辅助函数为这个非常用例。

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 

在我看来,利用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' 

请注意,这两个设置可写为false,并使用Object.freeze在v8中有一个巨大的性能损失 – https://bugs.chromium.org/p/v8/issues/detail?id=1858和http://jsperf.com /性能冰冻对象

ES6的方式。

在foo.js中导出

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

导入bar.js

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

我发现Dominicbuild议的解决scheme是最好的解决scheme,但它仍然遗漏了“const”声明的一个特性。 当用“const”关键字在JS中声明一个常量时,在parsing时检查常量的存在,而不是在运行时检查。 所以如果你在代码的某个地方拼错了常量的名字,当你尝试启动你的node.js程序的时候会出现错误。 这是一个更好的错误检查。

如果使用像Dominic这样的define()函数来定义这个常量,那么如果拼错这个常量,那么你将不会得到一个错误,而且这个拼写错误的常量的值将会是未定义的(这会导致debugging头痛)。

但我想这是最好的,我们可以得到。

另外,在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 

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

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

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

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

或者,您可以将“常量”值分组到本地对象中,并导出一个函数,该函数返回此对象的浅表副本。

 var constants = { FOO: "foo" } module.exports = function() { return Object.assign({}, constants) } 

那么是否有人重新分配FOO并不重要,因为这只会影响他们的本地副本。

由于Node.js使用的是CommonJS模式,所以你只能在模块之间共享variables,通过module.exports或者像在浏览器中一样设置一个全局variables,而不是使用你使用global.your_var = value;窗口global.your_var = value;

我最终通过导出一个具有匿名getter函数的冻结对象而不是常量本身来做到这一点。 这减less了由于常量名称的简单拼写而引入的讨厌错误的风险,因为在错字的情况下将会抛出运行时错误。 这里是一个完整的例子,它也使用常量的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 }); 

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

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

我build议用webpack来做 (假设你使用的是webpack)。

定义常量与设置webpackconfiguration文件一样简单:

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

这样你就可以在你的源代码之外定义它们,它们将在你的所有文件中可用。

我不认为从模块入侵GLOBAL空间是一种好的做法,但是在可能严格需要实施的情况下:

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

必须考虑到这种资源的影响。 如果没有对这些常数进行适当的命名,覆盖已经定义了全局variables的风险是真实的。