简体   繁体   English

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

[英]How do you share constants in NodeJS modules?

Currently I'm doing this:目前我正在这样做:

foo.js foo.js

const FOO = 5;

module.exports = {
    FOO: FOO
};

And using it in bar.js :并在bar.js使用它:

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

Is there a better way to do this?有一个更好的方法吗? It feels awkward to declare the constant in the exports object.在exports对象中声明常量感觉很尴尬。

In my opinion, utilizing Object.freeze allows for a DRYer and more declarative style.在我看来,使用Object.freeze可以实现 DRYer 和更具声明性的风格。 My preferred pattern is:我的首选模式是:

./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'

Outdated Performance Warning过时的性能警告

The following issue was fixed in v8 in Jan 2014 and is no longer relevant to most developers:以下问题已于 2014 年 1 月在 v8 中修复,并且不再与大多数开发人员相关:

Be aware that both setting writable to false and using Object.freeze have a massive performance penalty in v8 - https://bugs.chromium.org/p/v8/issues/detail?id=1858 and http://jsperf.com/performance-frozen-object请注意,将 writable 设置为 false 和使用 Object.freeze 在 v8 中都有巨大的性能损失 - https://bugs.chromium.org/p/v8/issues/detail?id=1858http://jsperf.com /性能冻结对象

Technically, const is not part of the ECMAScript specification.从技术上讲, const不是 ECMAScript 规范的一部分。 Also, using the "CommonJS Module" pattern you've noted, you can change the value of that "constant" since it's now just an object property.此外,使用您注意到的“CommonJS 模块”模式,您可以更改该“常量”的值,因为它现在只是一个对象属性。 (not sure if that'll cascade any changes to other scripts that require the same module, but it's possible) (不确定这是否会将任何更改级联到需要相同模块的其他脚本,但有可能)

To get a real constant that you can also share, check out Object.create , Object.defineProperty , and Object.defineProperties .要获得您也可以共享的真正常量,请查看Object.createObject.definePropertyObject.defineProperties If you set writable: false , then the value in your "constant" cannot be modified.如果设置writable: false ,则无法修改“常量”中的值。 :) :)

It's a little verbose, (but even that can be changed with a little JS) but you should only need to do it once for your module of constants.它有点冗长,(但即使可以用一点 JS 来改变),但你应该只需要为你的常量模块做一次。 Using these methods, any attribute that you leave out defaults to false .使用这些方法,您省略的任何属性都默认为false (as opposed to defining properties via assignment, which defaults all the attributes to true ) (与通过赋值定义属性相反,它将所有属性默认为true

So, hypothetically, you could just set value and enumerable , leaving out writable and configurable since they'll default to false , I've just included them for clarity.因此,假设您可以只设置valueenumerable ,而writablewritable configurable因为它们将默认为false ,为了清楚起见,我只是将它们包括在内。

Update - I've create a new module ( node-constants ) with helper functions for this very use-case.更新- 我为这个用例创建了一个带有辅助函数的新模块( node-constants )。

constants.js -- Good constants.js -- 好

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

constants.js -- Better constants.js -- 更好

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

define("PI", 3.14);

script.js脚本.js

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

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

ES6 way. ES6 方式。

export in foo.js在 foo.js 中导出

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

import in bar.js在 bar.js 中导入

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

You can explicitly export it to the global scope with global.FOO = 5 .您可以使用global.FOO = 5将其显式导出到全局范围。 Then you simply need to require the file, and not even save your return value.然后你只需要 require 文件,甚至不保存你的返回值。

But really, you shouldn't do that.但实际上,你不应该那样做。 Keeping things properly encapsulated is a good thing.把事情妥善地封装起来是一件好事。 You have the right idea already, so keep doing what you're doing.你已经有了正确的想法,所以继续做你正在做的事情。

I found the solution Dominic suggested to be the best one, but it still misses one feature of the "const" declaration.我发现 Dominic 建议的解决方案是最好的解决方案,但它仍然遗漏了“const”声明的一项功能。 When you declare a constant in JS with the "const" keyword, the existence of the constant is checked at parse time, not at runtime.当您在 JS 中使用“const”关键字声明一个常量时,会在解析时而不是在运行时检查该常量是否存在。 So if you misspelled the name of the constant somewhere later in your code, you'll get an error when you try to start your node.js program.因此,如果您在代码的后面某处拼错了常量的名称,则在尝试启动 node.js 程序时会出现错误。 Which is a far more better misspelling check.这是一个更好的拼写错误检查。

If you define the constant with the define() function like Dominic suggested, you won't get an error if you misspelled the constant, and the value of the misspelled constant will be undefined (which can lead to debugging headaches).如果您像 Dominic 建议的那样使用 define() 函数定义常量,那么如果您拼错了常量,就不会出现错误,并且拼错的常量的值将是未定义的(这会导致调试问题)。

But I guess this is the best we can get.但我想这是我们能得到的最好的。

Additionally, here's a kind of improvement of Dominic's function, in constans.js:此外,这是对多米尼克功能的一种改进,在 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;

In this way you can use the define() function in other modules, and it allows you to define constants both inside the constants.js module and constants inside your module from which you called the function.通过这种方式,您可以在其他模块中使用define() 函数,它允许您在constants.js 模块中定义常量以及在调用该函数的模块中定义常量。 Declaring module constants can then be done in two ways (in script.js).然后可以通过两种方式(在 script.js 中)声明模块常量。

First:第一的:

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

Second:第二:

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

Also, if you want the define() function to be called only from the constants module (not to bloat the global object), you define it like this in constants.js:此外,如果您希望仅从常量模块调用 define() 函数(而不是使全局对象膨胀),您可以在 constants.js 中像这样定义它:

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

and use it like this in script.js:并在 script.js 中像这样使用它:

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

From previous project experience, this is a good way:从以前的项目经验来看,这是一个很好的方法:

In the constants.js:在 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

In main.js (or app.js, etc.), use it as below:在 main.js(或 app.js 等)中,使用如下:

// main.js

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

console.log(constants.key1);

console.dir(constants.key3);

import and export (prob need something like babel as of 2018 to use import) importexport (可能需要像 2018 年的 babel 之类的东西才能使用导入)

types.js类型.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 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import

I think that const solves the problem for most people looking for this anwwer.我认为const解决了大多数寻找这个答案的人的问题。 If you really need an immutable constant, look into the other answers.如果您真的需要一个不可变的常量,请查看其他答案。 To keep everything organized I save all constants on a folder and then require the whole folder.为了保持一切井井有条,我将所有常量保存在一个文件夹中,然后需要整个文件夹。

src/main.js file src/main.js 文件

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

src/consts_folder/index.js src/consts_folder/index.js

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


module.exports = {
  deal,
  note
}

Ps.附言。 here the deal and note will be first level on the main.js这里的dealnote将是 main.js 的第一级

src/consts_folder/note.js src/consts_folder/note.js

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

Ps.附言。 obj will be second level on the main.js obj将是 main.js 的第二级

src/consts_folder/deal.js src/consts_folder/deal.js

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

Ps.附言。 str will be second level on the main.js str将是 main.js 的第二级

Final result on main.js file: main.js 文件的最终结果:

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

{ deal: { str: 'I\\'ma deal string' }, { 交易: { str: '我是交易字符串' },

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

note: { obj: { type: 'object', description: 'I\\'ma note object' } }注意:{ 对象:{ 类型:'对象',描述:'我是一个笔记对象'}}

As an alternative, you can group your "constant" values in a local object, and export a function that returns a shallow clone of this object.作为替代方案,您可以将“常量”值分组到一个本地对象中,并导出一个返回此对象的浅层克隆的函数。

var constants = { FOO: "foo" }

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

Then it doesn't matter if someone re-assigns FOO because it will only affect their local copy.那么是否有人重新分配 FOO 并不重要,因为它只会影响他们的本地副本。

I ended up doing this by exporting a frozen object with anonymous getter functions, rather than the constants themselves.我最终通过导出具有匿名 getter 函数的冻结对象而不是常量本身来完成此操作。 This reduces the risk of nasty bugs introduced due to a simple typo of the const name, as a runtime error will be thrown in case of a typo.这降低了由于 const 名称的简单拼写错误而引入的严重错误的风险,因为在拼写错误的情况下将引发运行时错误。 Here's a full example that also uses ES6 Symbols for the constants, ensuring uniqueness, and ES6 arrow functions.这是一个完整的例子,它也使用 ES6 符号作为常量,确保唯一性,以及 ES6 箭头函数。 Would appreciate feedback if anything in this approach seems problematic.如果这种方法中的任何内容似乎有问题,我们将不胜感激。

'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
});

Since Node.js is using the CommonJS patterns, you can only share variables between modules with module.exports or by setting a global var like you would in the browser, but instead of using window you use global.your_var = value;由于 Node.js 使用的是 CommonJS 模式,因此您只能使用module.exports或通过像在浏览器中那样设置全局变量来在模块之间共享变量,但是您使用global.your_var = value;代替 window global.your_var = value; . .

I recommend doing it with webpack (assumes you're using webpack).我建议使用webpack (假设您使用的是 webpack)。

Defining constants is as simple as setting the webpack config file:定义常量就像设置 webpack 配置文件一样简单:

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

This way you define them outside your source, and they will be available in all your files.通过这种方式,您可以在源之外定义它们,并且它们将在您的所有文件中可用。

I don't think is a good practice to invade the GLOBAL space from modules, but in scenarios where could be strictly necessary to implement it:我认为从模块侵入 GLOBAL 空间不是一个好习惯,但在可能严格需要实现它的场景中:

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

It has to be considered the impact of this resource.必须考虑该资源的影响。 Without proper naming of those constants, the risk of OVERWRITTING already defined global variables, is something real.如果没有正确命名这些常量,覆盖已经定义的全局变量的风险是真实存在的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM