Coder Perfect

In NodeJS modules, how do you exchange constants?

Problem

This is what I’m doing right now:

foo.js

const FOO = 5;

module.exports = {
    FOO: FOO
};

And using it in bar. js:

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

Is there a more efficient method to accomplish this? Declaring the constant in the exports object feels weird.

Asked by Tower

Solution #1

Using Object.freeze, in my opinion, enables for a DRYer and more declarative style. The following is my favored pattern:

module.exports = Object.freeze({
    MY_CONSTANT: 'some value',
    ANOTHER_CONSTANT: 'another value'
});
var constants = require('./constants');

console.log(constants.MY_CONSTANT); // 'some value'

constants.MY_CONSTANT = 'some other value';

console.log(constants.MY_CONSTANT); // 'some value'

The following issue was resolved in v8 in January 2014, and it no longer affects most developers:

Setting readable to false and using Object.freeze both have a significant performance penalty in v8 – see https://bugs.chromium.org/p/v8/issues/detail?id=1858 and http://jsperf.com/performance-frozen-object for more information.

Answered by Spain Train

Solution #2

Const isn’t technically part of the ECMAScript specification. You may also alter the value of that “constant” using the “CommonJS Module” technique you mentioned, because it’s now just an object property. (I’m not sure if this will affect other scripts that use the same module, but it’s conceivable)

Check out Object.create, Object.defineProperty, and Object.defineProperties to get a true constant that you can share. If writable: false is set, the value of your “constant” can’t be changed. 🙂

It’s a touch verbose (though that can be modified with little JS), but you should only have to do it once for your constants module. Any attribute that you leave out while using these methods defaults to false. (as opposed to assigning properties, which sets all attributes to true by default)

So you could theoretically only set value and enumerable, leaving out writable and configurable because they’ll default to false; I’ve added them for clarity.

Update: For this specific use-case, I’ve created a new module (node-constants) containing helpful functions.

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

define("PI", 3.14);
var constants = require("./constants");

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

Answered by Dominic Barnes

Solution #3

ES6 way.

export in foo.js

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

import in bar.js

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

Answered by Diego Mello

Solution #4

With global, you can explicitly export it to the global scope. FOO equals 5. Then all you have to do is require the file and forget about saving your return value.

However, you should not do so. It’s a good idea to keep everything well contained. Keep doing what you’re doing because you have the right concept.

Answered by Alex Wayne

Solution #5

ne, but one feature of the “const” declaration is still missing. The existence of a constant declared using the “const” keyword in JS is tested at parsing time rather than runtime. When you try to run your node.js program, you’ll get an error if the name of the constant is misspelled somewhere else in your code. That’s a far superior misspelling check.

If you use the define() function to declare the constant, you won’t get an error if you misspell the variable, and the value of the misspelled variable will be undefined (which can lead to debugging headaches).

However, I suppose this is the best we can do.

In constans.js, there’s also a kind of improvement to Dominic’s function:

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;

You can use the define() method in other modules this manner, and it allows you to define constants both within the constants.js module and within the module from which the function was called. There are two ways to declare module constants (in 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

In constants.js, you may also define the define() function to be called exclusively from the constants module (in order to avoid bloating the global object):

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

and use it in a script like this js:

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

Answered by xmak

Post is based on https://stackoverflow.com/questions/8595509/how-do-you-share-constants-in-nodejs-modules