繁体   English   中英

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

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

目前我正在这样做:

foo.js

const FOO = 5;

module.exports = {
    FOO: FOO
};

并在bar.js使用它:

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

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

在我看来,使用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 中修复,并且不再与大多数开发人员相关:

请注意,将 writable 设置为 false 和使用 Object.freeze 在 v8 中都有巨大的性能损失 - https://bugs.chromium.org/p/v8/issues/detail?id=1858http://jsperf.com /性能冻结对象

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

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

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

因此,假设您可以只设置valueenumerable ,而writablewritable 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);

脚本.js

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

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

ES6 方式。

在 foo.js 中导出

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

在 bar.js 中导入

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

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

但实际上,你不应该那样做。 把事情妥善地封装起来是一件好事。 你已经有了正确的想法,所以继续做你正在做的事情。

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

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

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

此外,这是对多米尼克功能的一种改进,在 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

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

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

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

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

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

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

importexport (可能需要像 2018 年的 babel 之类的东西才能使用导入)

类型.js

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

我的应用程序

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"
}

附言。 obj将是 main.js 的第二级

src/consts_folder/deal.js

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

附言。 str将是 main.js 的第二级

main.js 文件的最终结果:

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

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

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

注意:{ 对象:{ 类型:'对象',描述:'我是一个笔记对象'}}

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

var constants = { FOO: "foo" }

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

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

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

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

我建议使用webpack (假设您使用的是 webpack)。

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

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

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

暂无
暂无

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

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