Comment partagez-vous les constantes dans les modules NodeJS?


240

Actuellement, je fais ceci:

foo.js

const FOO = 5;

module.exports = {
    FOO: FOO
};

Et l'utiliser dans bar.js:

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

Y a-t-il une meilleure manière de faire cela? Il est gênant de déclarer la constante dans l'objet exports.


6
Si vous voulez l'exporter, vous le mettez dans le exports. Qu'est-ce qui est gênant?
Alex Wayne

5
Je suis habitué à C # et PHP. Je suppose que je dois juste m'habituer à définir chaque constante deux fois. Peut-être qu'à l'avenir, nous aurons export const FOO = 5;.
Tower

1
@Tower L'avenir est maintenant (ES2015)! 2ality.com/2014/09/…
Train d'Espagne du

1
Est-ce fonctionnellement différent du plus concis module.exports={FOO:5};?
Joe Lapp

3
Cela ne se sent pas seulement gêné, ce n'est plus une constante
Ini

Réponses:


97

Vous pouvez l'exporter explicitement vers la portée globale avec global.FOO = 5. Il vous suffit ensuite d'exiger le fichier et de ne même pas enregistrer votre valeur de retour.

Mais vraiment, vous ne devriez pas faire ça. Garder les choses correctement encapsulées est une bonne chose. Vous avez déjà la bonne idée, alors continuez à faire ce que vous faites.


51
Je suis désolé de le faire, mais -1 pour mieux savoir mais ne pas fournir une (meilleure) solution alternative; (Re: "Mais vraiment, vous ne devriez pas faire ça. Garder les choses correctement encapsulées est une bonne chose.")
Merci

22
Si toute la communauté du développement logiciel pensait de cette façon, nous utiliserions toujours des punchards. Heureusement, il y a quelques francs-tireurs qui savent quand il vaut mieux enfreindre les règles folles que nous nous imposons. Si l'encapsulation est utile, utilisez-la. Si c'est une nounou nerveuse qui vous empêche de faire votre travail, licenciez la nounou nerveuse et continuez.
non synchronisé

22
@naomik (temps de réponse très tardif) La vraie raison pour laquelle je n'ai pas fourni de meilleure solution est que l'OP connaît déjà la solution. Encapsulez les choses dans leur propre module et exigez-les si nécessaire.
Alex Wayne

1
Alors ce n'est pas une réponse réelle et devrait plutôt être un commentaire d'explication indiquant "vous faites assez bien, les alternatives sont mauvaises" ..
Andrey Popov

1
Mauvaise application de l'encapsulation. Lorsqu'une classe utilise des valeurs spéciales comme indicateurs et leur donne un nom, vous VOULEZ le partager avec tout code utilisant cette classe.
grantwparks

314

À mon avis, l'utilisation Object.freezepermet un style plus sec et plus déclaratif. Mon modèle préféré est:

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

Avertissement de performances obsolètes

Le problème suivant a été corrigé dans la v8 en janvier 2014 et n'est plus pertinent pour la plupart des développeurs:

Sachez que la définition de l'écriture en écriture sur false et l'utilisation d'Object.freeze ont une énorme baisse de performances dans la v8 - https://bugs.chromium.org/p/v8/issues/detail?id=1858 et http://jsperf.com / performance-gelé-objet


4
Bon cas d'utilisation pour Object.freeze!
Estus Flask

À quoi devrait-il ressembler si j'ai besoin d'exporter des constantes et des fonctions? Dois-je aussi mettre des fonctions dans le bloc d'arrêt?
Tom

3
cette approche est meilleure car la saisie semi-automatique IDE fonctionne avec elle.
David A

3
C'est une excellente réponse, mais cela pourrait détourner les gens de cette approche en raison de l'avertissement obsolète sur les performances de la v8 à la fin. Veuillez envisager de supprimer l'avertissement.
sampathsris

4
Merci @Krumia! Je l'ai mis à jour, mais j'ai laissé le texte d'avertissement original uniquement pour le contexte historique (et parce que certains de ces commentaires n'auraient pas de sens sans cela).
Espagne Train

163

Techniquement, constne fait pas partie de la spécification ECMAScript. En outre, en utilisant le modèle "Module CommonJS" que vous avez noté, vous pouvez modifier la valeur de cette "constante" car il ne s'agit désormais que d'une propriété d'objet. (je ne sais pas si cela apportera des modifications aux autres scripts qui nécessitent le même module, mais c'est possible)

Pour obtenir une constante réelle que vous pouvez partager, consultez Object.create, Object.definePropertyet Object.defineProperties. Si vous définissez writable: false, la valeur de votre "constante" ne peut pas être modifiée. :)

C'est un peu verbeux, (mais même cela peut être changé avec un peu de JS) mais vous ne devriez le faire qu'une seule fois pour votre module de constantes. En utilisant ces méthodes, tout attribut que vous omettez par défaut false. (par opposition à la définition de propriétés via l'affectation, qui attribue par défaut tous les attributs à true)

Donc, hypothétiquement, vous pouvez simplement définir valueet enumerable, en laissant de côté writableet configurablecomme ils seront par défaut false, je les ai juste inclus pour plus de clarté.

Mise à jour - J'ai créé un nouveau module ( constantes de nœuds ) avec des fonctions d'assistance pour ce cas d'utilisation très.

constants.js - Bon

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

constants.js - Mieux

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
@AntoineHedgecock Ce n'est pas nécessaire, consultez la documentation sur Object.defineProperty(). Toutes les propriétés non spécifiées sont supposées falsedans ce contexte.
Dominic Barnes

6
A noter également, Object.freeze ()
damianb

1
C'est la meilleure réponse à cette question. +1. Si je le pouvais, je le voterais davantage.
Ryan

1
Magnifique réponse, une solution très élégante et sûre.
Alex

1
@SpainTrain Cela semble avoir été corrigé par codereview.chromium.org/135903014
Grinde

101

Manière ES6.

exporter dans foo.js

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

importer dans bar.js

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

41
Oui. Stack Overflow a besoin d'un moyen de déprécier les réponses obsolètes.
Rick Jolly

7
Notez que c'est le constin bar.jsqui impose l'immuabilité de la variable déstructurée, pas le constin foo.js. C'est, on peut utiliser let {FOO} =dans bar.jset muter la variable « constante ». AFAIK, pour imposer l'immuabilité des exportations, il faut encore des modules ES ou Object.freeze.
Espagne Train

On peut aussi changer à l' FOOintérieur foo.js.
lima_fil

16

J'ai trouvé que la solution suggérée par Dominic était la meilleure, mais il manque toujours une caractéristique de la déclaration "const". Lorsque vous déclarez une constante dans JS avec le mot clé "const", l'existence de la constante est vérifiée au moment de l'analyse, pas au moment de l'exécution. Donc, si vous avez mal orthographié le nom de la constante quelque part plus tard dans votre code, vous obtiendrez une erreur lorsque vous essayez de démarrer votre programme node.js. Ce qui est un bien meilleur contrôle orthographique.

Si vous définissez la constante avec la fonction define () comme Dominic l'a suggéré, vous n'obtiendrez pas d'erreur si vous avez mal orthographié la constante, et la valeur de la constante mal orthographiée sera indéfinie (ce qui peut conduire au débogage de maux de tête).

Mais je suppose que c'est le meilleur que nous puissions obtenir.

De plus, voici une sorte d'amélioration de la fonction de Dominic, dans 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;

De cette façon, vous pouvez utiliser la fonction define () dans d'autres modules, et cela vous permet de définir des constantes à la fois à l'intérieur du module constants.js et des constantes à l'intérieur de votre module à partir duquel vous avez appelé la fonction. La déclaration des constantes de module peut alors se faire de deux manières (dans script.js).

Première:

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

Seconde:

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

De plus, si vous souhaitez que la fonction define () soit appelée uniquement à partir du module de constantes (pour ne pas gonfler l'objet global), vous la définissez comme ceci dans constants.js:

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

et utilisez-le comme ceci dans script.js:

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

11

De l'expérience de projet précédente, c'est un bon moyen:

Dans le 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

Dans main.js (ou app.js, etc.), utilisez-le comme ci-dessous:

// main.js

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

console.log(constants.key1);

console.dir(constants.key3);

8

Je pense que cela constrésout le problème pour la plupart des personnes à la recherche de cette réponse. Si vous avez vraiment besoin d'une constante immuable, examinez les autres réponses. Pour garder tout organisé, j'enregistre toutes les constantes dans un dossier, puis j'ai besoin de tout le dossier.

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

Ps. ici le dealet notesera de premier niveau sur le main.js

src / consts_folder / note.js

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

Ps. objsera de deuxième niveau sur le main.js

src / consts_folder / deal.js

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

Ps. strsera de deuxième niveau sur le main.js

Résultat final sur le fichier main.js:

console.log(constants.deal); Sortie:

{deal: {str: 'I \' ma deal string '},

console.log(constants.note); Sortie:

note: {obj: {type: 'objet', description: 'Je suis un objet note'}}



4

Vous pouvez également regrouper vos valeurs "constantes" dans un objet local et exporter une fonction qui renvoie un clone superficiel de cet objet.

var constants = { FOO: "foo" }

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

Ensuite, peu importe si quelqu'un réaffecte FOO car cela n'affectera que sa copie locale.


ou tout simplement module.exports = () => ({FOO: "foo", BAR: "bar"});
Björn Grambow

3

Étant donné que Node.js utilise les modèles CommonJS, vous pouvez uniquement partager des variables entre les modules avec module.exportsou en définissant une variable globale comme vous le feriez dans le navigateur, mais au lieu d'utiliser la fenêtre que vous utilisez global.your_var = value;.


2

J'ai fini par le faire en exportant un objet figé avec des fonctions getter anonymes, plutôt que les constantes elles-mêmes. Cela réduit le risque de bugs méchants introduits en raison d'une simple faute de frappe du nom const, car une erreur d'exécution sera levée en cas de faute de frappe. Voici un exemple complet qui utilise également les symboles ES6 pour les constantes, garantissant l'unicité et les fonctions fléchées ES6. J'apprécierais les commentaires si quelque chose dans cette approche semble problématique.

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

0

Je recommande de le faire avec webpack (en supposant que vous utilisez webpack).

La définition des constantes est aussi simple que de définir le fichier de configuration du webpack:

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

De cette façon, vous les définissez en dehors de votre source, et ils seront disponibles dans tous vos fichiers.


0

Je ne pense pas que ce soit une bonne pratique d'envahir l'espace GLOBAL à partir de modules, mais dans des scénarios où cela pourrait être strictement nécessaire pour l'implémenter:

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

Il faut considérer l'impact de cette ressource. Sans une dénomination correcte de ces constantes, le risque de RÉÉCRITURE des variables globales déjà définies, est quelque chose de réel.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.