คุณจะแบ่งปันค่าคงที่ในโมดูล NodeJS ได้อย่างไร


240

ขณะนี้ฉันกำลังทำสิ่งนี้:

foo.js

const FOO = 5;

module.exports = {
    FOO: FOO
};

และใช้มันในbar.js:

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

มีวิธีที่ดีกว่าในการทำเช่นนี้? รู้สึกอึดอัดใจที่จะประกาศค่าคงที่ในวัตถุส่งออก


6
exportsหากคุณต้องการที่จะส่งออกมันคุณใส่ไว้ใน เกิดอะไรขึ้นกับสิ่งนั้น?
Alex Wayne

5
ฉันคุ้นเคยกับ C # และ PHP ฉันเดาว่าฉันแค่ต้องคุ้นเคยกับการกำหนดค่าคงที่สองครั้ง export const FOO = 5;บางทีในอนาคตเราจะมี
หอคอย

1
@Tower อนาคตตอนนี้ (ES2015)! 2ality.com/2014/09/…
รถไฟสเปน

1
ฟังก์ชั่นนี้แตกต่างจากความรัดกุมมากกว่านี้module.exports={FOO:5};ไหม
Joe Lapp

3
มันไม่เพียง แต่รู้สึกว่า Akward มันไม่คงที่อีกต่อไปแล้ว
Ini

คำตอบ:


97

global.FOO = 5คุณอย่างชัดเจนสามารถส่งออกไปยังขอบเขตทั่วโลกด้วย จากนั้นคุณเพียงแค่ต้องการไฟล์และไม่ต้องบันทึกค่าส่งคืน

แต่จริงๆแล้วคุณไม่ควรทำอย่างนั้น การเก็บรักษาสิ่งต่าง ๆ อย่างถูกต้องเป็นสิ่งที่ดี คุณมีความคิดที่ถูกต้องอยู่แล้วดังนั้นทำในสิ่งที่คุณกำลังทำอยู่


51
ฉันขอโทษที่ทำสิ่งนี้ แต่ -1 สำหรับการรู้จักดีขึ้น แต่ไม่ได้ให้โซลูชันทางเลือกที่ดีกว่า (re: "แต่จริง ๆ แล้วคุณไม่ควรทำอย่างนั้นการรักษาสิ่งที่ห่อหุ้มอย่างถูกต้องเป็นสิ่งที่ดี")
ขอบคุณ

22
หากชุมชนการพัฒนาซอฟต์แวร์ทั้งหมดคิดเช่นนี้เราจะยังคงใช้หมัดเด็ด โชคดีที่มีผู้ผลิต mavericks เพียงไม่กี่คนที่รู้ว่าเมื่อไรจะดีกว่าที่จะทำลายกฎที่บ้าคลั่งที่เรากำหนดไว้กับตัวเราเอง หากการห่อหุ้มมีประโยชน์ให้ใช้ หากพี่เลี้ยงประสาทหยุดคุณทำงานให้ยิงพี่เลี้ยงประสาทและไปด้วย
ไนซ์

22
@naomik (เวลาตอบกลับล่าช้ามาก) เหตุผลที่แท้จริงที่ฉันไม่ได้ให้วิธีแก้ปัญหาที่ดีกว่านั้นเพราะ OP รู้วิธีแก้ปัญหาอยู่แล้ว ห่อหุ้มสิ่งต่าง ๆ ในหน่วยของตนเองและกำหนดให้จำเป็นเมื่อจำเป็น
Alex Wayne

1
แล้วนี่ไม่ใช่คำตอบที่แท้จริงและควรเป็นคำอธิบายความคิดเห็นที่ระบุว่า "คุณทำได้ดีพอมีทางเลือกอื่นที่ไม่ดี" ..
Andrey Popov

1
แอปพลิเคชันที่ไม่ถูกต้องของการห่อหุ้ม เมื่อชั้นเรียนใช้ค่าพิเศษเป็นตัวบ่งชี้และตั้งชื่อพวกเขาคุณต้องการแบ่งปันด้วยรหัสใด ๆ ที่ใช้ประโยชน์จากชั้นเรียนนั้น
grantwparks

314

ในความคิดของฉันการใช้Object.freezeช่วยให้สไตล์ที่แห้งและเปิดเผยมากขึ้น รูปแบบที่ฉันชอบคือ:

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

คำเตือนประสิทธิภาพที่ล้าสมัย

ปัญหาต่อไปนี้ได้รับการแก้ไขใน v8 ในเดือนมกราคม 2014 และไม่เกี่ยวข้องกับนักพัฒนาส่วนใหญ่อีกต่อไป:

โปรดทราบว่าทั้งการตั้งค่าที่เขียนได้เป็นเท็จและการใช้ Object.freeze มีโทษประสิทธิภาพสูงใน v8 - https://bugs.chromium.org/p/v8/issues/detail?id=1858และhttp://jsperf.com ประสิทธิภาพ / แช่แข็งวัตถุ


4
กรณีใช้งานที่ดีสำหรับ Object.freeze!
Estus Flask

ฉันควรทำอย่างไรหากฉันต้องการส่งออกค่าคงที่และฟังก์ชั่น ฉันควรใส่ฟังก์ชั่นในช่องแช่แข็งด้วยหรือไม่
Tom

3
วิธีนี้ดีกว่าเนื่องจากการเติมข้อความอัตโนมัติ IDE ทำงานร่วมกับมันได้
เดวิด

3
นี่เป็นคำตอบที่ดี แต่อาจทำให้ผู้คนไม่สนใจแนวทางนี้เนื่องจากคำเตือนที่ล้าสมัยเกี่ยวกับประสิทธิภาพของ v8 ในตอนท้าย โปรดลองลบคำเตือนออก
sampathsris

4
ขอบคุณ @Krumia! ฉันได้อัปเดตแล้ว แต่ทิ้งข้อความเตือนดั้งเดิมไว้เพียงเพื่อบริบททางประวัติศาสตร์ (และเนื่องจากความคิดเห็นเหล่านี้บางรายการอาจไม่เหมาะสมหากไม่มี)
รถไฟสเปน

163

ในทางเทคนิคconstไม่ได้เป็นส่วนหนึ่งของข้อมูลจำเพาะ ECMAScript นอกจากนี้การใช้รูปแบบ "โมดูล CommonJS" ที่คุณจดบันทึกไว้คุณสามารถเปลี่ยนค่าของ "ค่าคงที่" เนื่องจากตอนนี้เป็นเพียงคุณสมบัติของวัตถุ (ไม่แน่ใจว่าจะทำให้การเปลี่ยนแปลงใด ๆ กับสคริปต์อื่นที่ต้องใช้โมดูลเดียวกัน แต่เป็นไปได้)

ที่จะได้รับอย่างต่อเนื่องจริงที่คุณยังสามารถแบ่งปันให้ตรวจสอบObject.create, และObject.defineProperty Object.definePropertiesหากคุณตั้งค่าwritable: falseจะไม่สามารถแก้ไขค่าใน "คงที่" ของคุณได้ :)

มันเป็น verbose เล็กน้อย (แต่ถึงแม้จะสามารถเปลี่ยนแปลงได้ด้วย JS เล็ก ๆ ) แต่คุณควรจะทำเพียงครั้งเดียวสำหรับโมดูลของค่าคงที่ของคุณ โดยใช้วิธีการเหล่านี้แอตทริบิวต์ใด ๆ falseที่คุณออกจากค่าเริ่มต้น (ตรงข้ามกับการกำหนดคุณสมบัติผ่านการมอบหมายซึ่งเป็นค่าเริ่มต้นของแอตทริบิวต์ทั้งหมดไปtrue)

ดังนั้นสมมุติคุณสามารถตั้งค่าvalueและenumerableออกไปwritableและconfigurableเนื่องจากพวกเขาจะเริ่มต้นเพื่อfalseฉันได้รวมไว้เพื่อความชัดเจน

อัปเดต - ฉันได้สร้างโมดูลใหม่ ( ค่าคงที่โหนด ) ที่มีฟังก์ชั่นตัวช่วยสำหรับกรณีการใช้งานนี้มาก

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
@AntoineHedgecock Object.defineProperty()ก็ไม่จำเป็นต้องตรวจสอบเอกสารประกอบใน คุณสมบัติทั้งหมดที่ไม่ได้ระบุจะถือว่าfalseในบริบทนี้
Dominic Barnes

6
สิ่งสำคัญด้วยเช่นกันคือ Object.freeze ()
damianb

1
นี่คือคำตอบที่ดีที่สุดของคำถามนี้ +1 ถ้าฉันทำได้ฉันจะลงคะแนนมากกว่านี้
Ryan

1
คำตอบที่ยอดเยี่ยมทางออกที่สวยงามและปลอดภัย
อเล็กซ์

1
@SpainTrain สิ่งนี้ดูเหมือนจะได้รับการแก้ไขโดยcodereview.chromium.org/135903014
Grinde

100

วิธี ES6

ส่งออกเป็น foo.js

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

นำเข้าใน bar.js

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

40
ใช่. Stack Overflow ต้องการวิธีในการคิดค่าเสื่อมราคาคำตอบที่ล้าสมัย
Rick Jolly

7
หมายเหตุว่ามันเป็นconstในbar.jsที่บังคับไม่เปลี่ยนรูปของตัวแปร destructured ไม่ได้ในconst foo.jsนั่นคือหนึ่งสามารถใช้let {FOO} =ในbar.jsและกลายพันธุ์ "คงที่" ตัวแปร AFAIK, Object.freezeการบังคับใช้การไม่เปลี่ยนรูปของการส่งออกหนึ่งยังคงต้องการอย่างใดอย่างหนึ่งหรือโมดูล
รถไฟสเปน

หนึ่งยังสามารถเปลี่ยนภายในFOO foo.js
lima_fil

16

ฉันพบวิธีแก้ปัญหาโดมินิกแนะนำให้ดีที่สุด แต่ก็ยังขาดคุณสมบัติหนึ่งของการประกาศ "const" เมื่อคุณประกาศค่าคงที่ใน JS ด้วยคีย์เวิร์ด "const" การมีอยู่ของค่าคงที่จะถูกตรวจสอบ ณ เวลาที่แจงไม่ใช่เวลารันไทม์ ดังนั้นหากคุณสะกดชื่อค่าคงที่ไว้ที่ใดที่หนึ่งในรหัสของคุณคุณจะได้รับข้อผิดพลาดเมื่อคุณพยายามเริ่มโปรแกรม node.js ซึ่งเป็นการตรวจสอบการสะกดคำผิดที่ดีกว่าไกลกว่ามาก

หากคุณกำหนดค่าคงที่ด้วยฟังก์ชั่น define () เช่น Dominic ที่แนะนำคุณจะไม่ได้รับข้อผิดพลาดหากคุณสะกดค่าคงที่และค่าของค่าคงที่ที่สะกดผิดนั้นจะไม่ได้กำหนดไว้ (ซึ่งอาจนำไปสู่

แต่ฉันเดาว่านี่เป็นสิ่งที่ดีที่สุดที่เราจะได้รับ

นอกจากนี้ยังเป็นการปรับปรุงฟังก์ชันของ Dominic ใน 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" );

11

จากประสบการณ์โครงการก่อนหน้านี้เป็นวิธีที่ดี:

ในค่าคงที่:

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

8

ฉันคิดว่านั่นconstแก้ปัญหาได้สำหรับคนส่วนใหญ่ที่กำลังมองหา anwwer นี้ หากคุณต้องการค่าคงที่ที่ไม่เปลี่ยนรูปแบบจริงๆให้ดูคำตอบอื่น ๆ เพื่อให้ทุกอย่างเป็นระเบียบฉันบันทึกค่าคงที่ทั้งหมดไว้ในโฟลเดอร์แล้วต้องใช้ทั้งโฟลเดอร์

ไฟล์ 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 ที่นี่dealและnoteจะเป็นระดับแรกใน main.js

src / consts_folder / note.js

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

ps objจะเป็นระดับที่สองใน main.js

src / consts_folder / deal.js

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

ps strจะเป็นระดับที่สองใน main.js

ผลสุดท้ายในไฟล์ main.js:

console.log(constants.deal); ouput:

{deal: {str: 'I \' ma ดีลสตริง '},

console.log(constants.note); ouput:

หมายเหตุ: {obj: {type: 'object', คำอธิบาย: 'I \' ma note object '}}



4

คุณสามารถจัดกลุ่มค่า "คงที่" ของคุณในวัตถุภายในเครื่องและส่งออกฟังก์ชันที่ส่งกลับโคลนแบบตื้นของวัตถุนี้

var constants = { FOO: "foo" }

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

จากนั้นไม่สำคัญว่ามีใครมอบหมาย FOO อีกครั้งเพราะจะมีผลกับสำเนาในเครื่องของตนเท่านั้น


หรือเพียงแค่ module.exports = () => ({FOO: "foo", BAR: "bar"});
Björn Grambow

3

ตั้งแต่ Node.js ใช้รูปแบบการ CommonJS คุณสามารถแบ่งปันตัวแปรระหว่างโมดูลกับmodule.exportsหรือโดยการตั้งค่า var ระดับโลกเช่นที่คุณต้องการในเบราว์เซอร์ global.your_var = value;แต่แทนที่จะใช้หน้าต่างที่คุณใช้


2

ฉันลงเอยด้วยการส่งออกวัตถุที่ถูกแช่แข็งด้วยฟังก์ชั่นผู้ทะเยอทะยานที่ไม่ระบุตัวตน สิ่งนี้จะช่วยลดความเสี่ยงของข้อผิดพลาดที่น่ารังเกียจที่เกิดขึ้นเนื่องจากการพิมพ์ผิดอย่างง่ายของชื่อ 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
});

0

ฉันแนะนำให้ทำกับwebpack (สมมติว่าคุณกำลังใช้ webpack)

การกำหนดค่าคงที่นั้นง่ายพอ ๆ กับการตั้งค่าไฟล์ webpack:

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

วิธีนี้คุณกำหนดไว้นอกแหล่งที่มาของคุณและพวกเขาจะสามารถใช้ได้ในไฟล์ทั้งหมดของคุณ


0

ฉันไม่คิดว่าเป็นแนวปฏิบัติที่ดีในการบุกรุกพื้นที่ GLOBAL จากโมดูล แต่ในสถานการณ์ที่อาจจำเป็นอย่างยิ่งที่จะต้องนำไปปฏิบัติ:

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

มันจะต้องได้รับการพิจารณาผลกระทบของทรัพยากรนี้ หากไม่มีการตั้งชื่อที่เหมาะสมของค่าคงที่เหล่านั้นความเสี่ยงของ OVERWRITTING ที่กำหนดตัวแปรทั่วโลกไว้แล้วนั้นเป็นของจริง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.