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

时间:2011-12-21 19:54:22

标签: javascript node.js

目前我正在这样做:

foo.js

const FOO = 5;

module.exports = {
    FOO: FOO
};

bar.js中使用它:

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

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

13 个答案:

答案 0 :(得分:238)

在我看来,利用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/performance-frozen-object

答案 1 :(得分:156)

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

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

它有点冗长,(但即使可以用一点JS来改变)但是你应该只为你的常量模块做一次。使用这些方法,您省略的任何属性都默认为false。 (而不是通过赋值定义属性,将所有属性默认为true

因此,假设您可以设置valueenumerable,而不会writableconfigurable,因为它们默认为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 :(得分:73)

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

但实际上,你不应该这样做。保持正确封装的东西是一件好事。你已经有了正确的想法,所以继续做你正在做的事情。

答案 3 :(得分:69)

ES6方式。

在foo.js中导出

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

导入bar.js

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

答案 4 :(得分:15)

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

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

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

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

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

答案 5 :(得分:8)

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

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

答案 6 :(得分:5)

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

聚苯乙烯。 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:'我是一个交易字符串'},

console.log(constants.note); 输出继电器:

  

注意:{obj:{type:'object',描述:'我是一个音符对象'}}

答案 7 :(得分:3)

作为替代方案,您可以对"常数"进行分组。本地对象中的值,并导出一个返回此对象的浅层克隆的函数。

var constants = { FOO: "foo" }

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

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

答案 8 :(得分:3)

importexport(问题需要像2018年的babel一样使用导入)

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

答案 9 :(得分:2)

由于Node.js正在使用CommonJS模式,您只能在module.exports模块之间共享变量,或者像在浏览器中那样设置全局变量,而不是使用窗口,而是使用{{1} }。

答案 10 :(得分:1)

我最终通过导出具有匿名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
});

答案 11 :(得分:0)

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

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

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

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

答案 12 :(得分:0)

我认为从模块入侵GLOBAL空间是一种很好的做法,但在实施它的情况下可能是绝对必要的:

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

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

相关问题