Làm thế nào để bạn chia sẻ hằng trong các mô-đun NodeJS?


238

Hiện tại tôi đang làm điều này:

foo.js

const FOO = 5;

module.exports = {
    FOO: FOO
};

Và sử dụng nó trong bar.js:

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

Có cách nào tốt hơn để làm điều này? Nó cảm thấy lúng túng khi tuyên bố hằng số trong đối tượng xuất khẩu.


6
Nếu bạn muốn xuất nó, bạn đặt nó vào exports. Có gì khó xử về điều đó?
Alex Wayne

5
Tôi đã quen với C # và PHP. Tôi đoán tôi chỉ cần làm quen với việc xác định mỗi hằng số hai lần. Có lẽ trong tương lai chúng ta sẽ có export const FOO = 5;.
Tháp

1
@Tower Tương lai là bây giờ (ES2015)! 2ality.com/2014/09/ Quảng
Tàu Tây Ban Nha

1
Đây có phải là chức năng khác với ngắn gọn hơn module.exports={FOO:5};?
Joe Lapp

3
Nó không chỉ cảm thấy bất ổn, nó không còn bất biến nữa
Ini

Câu trả lời:


95

Bạn rõ ràng có thể xuất nó ra phạm vi toàn cầu với global.FOO = 5. Sau đó, bạn chỉ cần yêu cầu tệp và thậm chí không lưu giá trị trả về của bạn.

Nhưng thực sự, bạn không nên làm điều đó. Giữ mọi thứ được đóng gói đúng cách là một điều tốt. Bạn đã có ý tưởng đúng rồi, vì vậy hãy tiếp tục làm những gì bạn đang làm.


51
Tôi rất tiếc phải làm điều này, nhưng -1 vì biết rõ hơn nhưng không cung cấp giải pháp thay thế (tốt hơn); (re: "Nhưng thực sự, bạn không nên làm điều đó. Giữ mọi thứ được gói gọn là một điều tốt.")
Cảm ơn bạn vào

22
Nếu toàn bộ cộng đồng phát triển phần mềm nghĩ theo cách này, chúng tôi vẫn sẽ sử dụng các trò đấm bốc. May mắn thay, có một vài mavericks ngoài kia biết khi nào nên phá vỡ các quy tắc điên rồ mà chúng ta áp đặt cho chính mình. Nếu đóng gói là hữu ích, sử dụng nó. Nếu đó là một người giữ trẻ lo lắng ngăn cản bạn thực hiện công việc của mình, hãy sa thải người giữ trẻ lo lắng và tiếp tục với nó.
không đồng bộ

22
@naomik (thời gian trả lời siêu muộn) Lý do thực sự khiến tôi không cung cấp giải pháp tốt hơn là vì OP đã biết giải pháp. Đóng gói những thứ trong mô-đun riêng của họ và yêu cầu chúng khi cần thiết.
Alex Wayne

1
Sau đó, đây không phải là một câu trả lời thực sự và thay vào đó là một nhận xét giải thích cho biết "bạn đang làm đủ tốt, các lựa chọn thay thế là xấu" ..
Andrey Popov

1
Ứng dụng sai của đóng gói. Khi một lớp sử dụng các giá trị đặc biệt làm chỉ báo và đặt tên cho chúng, bạn MUỐN chia sẻ điều đó với bất kỳ mã nào sử dụng lớp đó.
Grantwparks

313

Theo tôi, việc sử dụng Object.freezecho phép DRYer và phong cách khai báo hơn. Mẫu ưa thích của tôi là:

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

Cảnh báo hiệu suất lỗi thời

Sự cố sau đã được khắc phục trong v8 vào tháng 1 năm 2014 và không còn phù hợp với hầu hết các nhà phát triển:

Xin lưu ý rằng cả cài đặt có thể ghi thành sai và sử dụng Object.freeze đều bị phạt hiệu suất lớn trong v8 - https://bugs.chromium.org/p/v8/issues/detail?id=1858http://jsperf.com / hiệu suất-đông lạnh-đối tượng


4
Trường hợp sử dụng tốt cho Object.freeze!
Bình Estus

Nó sẽ trông như thế nào nếu tôi cần xuất cả hằng và hàm? Tôi có nên đặt chức năng trong khối đóng băng không?
Tom

3
Cách tiếp cận này tốt hơn vì IDE tự động hoàn thành hoạt động với nó.
David A

3
Đây là một câu trả lời tuyệt vời, nhưng nó có thể khiến mọi người tránh xa cách tiếp cận này vì cảnh báo lỗi thời về hiệu suất của v8 ở cuối. Vui lòng xem xét loại bỏ cảnh báo.
sampathsris

4
Cảm ơn @Krumia! Tôi đã cập nhật nó, nhưng để lại văn bản cảnh báo ban đầu chỉ cho bối cảnh lịch sử (và vì một số ý kiến ​​này sẽ không có ý nghĩa nếu không có nó).
Tàu Tây Ban Nha

163

Về mặt kỹ thuật, constkhông phải là một phần của đặc tả ECMAScript. Ngoài ra, bằng cách sử dụng mẫu "Mô-đun CommonJS" mà bạn đã lưu ý, bạn có thể thay đổi giá trị của "hằng số" đó vì giờ đây nó chỉ là một thuộc tính đối tượng. (không chắc chắn liệu điều đó có xếp tầng bất kỳ thay đổi nào đối với các tập lệnh khác yêu cầu cùng một mô-đun không, nhưng điều đó là có thể)

Để có được một hằng số thực sự mà bạn cũng có thể chia sẻ, kiểm tra Object.create, Object.definePropertyObject.defineProperties. Nếu bạn đặt writable: false, thì giá trị trong "hằng số" của bạn không thể được sửa đổi. :)

Đó là một chút dài dòng (nhưng thậm chí có thể thay đổi với một chút JS) nhưng bạn chỉ cần thực hiện một lần cho mô-đun hằng số của mình. Sử dụng các phương thức này, bất kỳ thuộc tính nào bạn bỏ mặc định false. (trái ngược với việc xác định các thuộc tính thông qua gán, mặc định tất cả các thuộc tính là true)

Vì vậy, theo giả thuyết, bạn chỉ có thể đặt valueenumerablebỏ qua writableconfigurablevì chúng sẽ mặc định false, tôi chỉ đưa chúng vào cho rõ ràng.

Cập nhật - Tôi đã tạo một mô-đun mới ( hằng số nút ) với các hàm trợ giúp cho trường hợp sử dụng này.

constants.js - Tốt

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

constants.js - Tốt hơn

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 Không cần thiết, hãy kiểm tra tài liệu trên Object.defineProperty(). Tất cả các thuộc tính không được chỉ định được giả định falsetrong bối cảnh này.
Đaminh Barnes

6
Cũng đáng chú ý, Object.freeze ()
damianb

1
Đây là câu trả lời tốt nhất của câu hỏi này. +1. Nếu tôi có thể tôi sẽ nâng cao nó hơn.
Ryan

1
Câu trả lời tuyệt vời, một giải pháp rất thanh lịch và an toàn.
Alex

1
@SpainTrain Điều này dường như đã được sửa bởi codereview.chromium.org/135903014
Grinde

100

Cách ES6.

xuất trong foo.js

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

nhập vào bar.js

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

40
Đúng. Stack Overflow cần một cách để khấu hao các câu trả lời lỗi thời.
Rick Jolly

7
Lưu ý rằng chính consttrong bar.jsđó thực thi tính bất biến của biến bị phá hủy, chứ không phải consttrong foo.js. Nghĩa là, người ta có thể sử dụng let {FOO} =trong bar.jsvà biến những "liên tục" biến. AFAIK, để thực thi tính bất biến của xuất khẩu, người ta vẫn cần các mô-đun ES hoặc Object.freeze.
Tàu Tây Ban Nha

Người ta cũng có thể thay đổi FOObên trong foo.js.
lima_fil

16

Tôi tìm thấy giải pháp mà Dominic đề xuất là giải pháp tốt nhất, nhưng nó vẫn bỏ lỡ một tính năng của khai báo "const". Khi bạn khai báo một hằng số trong JS với từ khóa "const", sự tồn tại của hằng số được kiểm tra tại thời điểm phân tích, không phải trong thời gian chạy. Vì vậy, nếu bạn viết sai tên của hằng số ở đâu đó trong mã của bạn, bạn sẽ gặp lỗi khi bạn cố gắng khởi động chương trình node.js của mình. Đó là một kiểm tra sai chính tả tốt hơn nhiều.

Nếu bạn xác định hằng số với hàm định nghĩa () như Dominic đã đề xuất, bạn sẽ không gặp lỗi nếu bạn viết sai chính tả và giá trị của hằng số sai chính tả sẽ không được xác định (có thể dẫn đến việc gỡ lỗi đau đầu).

Nhưng tôi đoán đây là điều tốt nhất chúng ta có thể nhận được.

Ngoài ra, đây là một loại cải tiến chức năng của Dominic, trong 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;

Theo cách này, bạn có thể sử dụng hàm định nghĩa () trong các mô-đun khác và nó cho phép bạn xác định các hằng cả bên trong mô-đun constants.js và các hằng số bên trong mô-đun mà bạn gọi là hàm. Khai báo các hằng mô-đun sau đó có thể được thực hiện theo hai cách (trong script.js).

Đầu tiên:

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

Thứ hai:

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

Ngoài ra, nếu bạn muốn hàm chỉ định () chỉ được gọi từ mô đun hằng (không làm phình to đối tượng toàn cầu), bạn định nghĩa nó như thế này trong constants.js:

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

và sử dụng nó như thế này trong script.js:

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

11

Từ kinh nghiệm dự án trước đây, đây là một cách tốt:

Trong 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

Trong main.js (hoặc app.js, v.v.), hãy sử dụng nó như dưới đây:

// main.js

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

console.log(constants.key1);

console.dir(constants.key3);

8

Tôi nghĩ rằng điều đó constgiải quyết vấn đề cho hầu hết mọi người tìm kiếm anwwer này. Nếu bạn thực sự cần một hằng số bất biến, hãy nhìn vào các câu trả lời khác. Để giữ mọi thứ ngăn nắp, tôi lưu tất cả các hằng số trên một thư mục và sau đó yêu cầu toàn bộ thư mục.

tập tin 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
}

Thi thiên ở đây dealnotesẽ là cấp độ đầu tiên trên main.js

src / consts_folder / note.js

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

Thi thiên objsẽ là cấp độ thứ hai trên main.js

src / consts_folder / deal.js

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

Thi thiên strsẽ là cấp độ thứ hai trên main.js

Kết quả cuối cùng trên tệp main.js:

console.log(constants.deal); Ouput:

{deal: {str: 'I \' ma thỏa thuận chuỗi '},

console.log(constants.note); Ouput:

lưu ý: {obj: {type: 'object', mô tả: 'I \' ma note object '}}



4

Thay vào đó, bạn có thể nhóm các giá trị "không đổi" của mình trong một đối tượng cục bộ và xuất một hàm trả về một bản sao nông của đối tượng này.

var constants = { FOO: "foo" }

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

Sau đó, sẽ không có vấn đề gì nếu ai đó gán lại FOO vì nó sẽ chỉ ảnh hưởng đến bản sao cục bộ của họ.


hoặc chỉ module.exports = () => ({FOO: "foo", BAR: "bar"});
Bjorn Grambow

3

Vì Node.js đang sử dụng các mẫu CommonJS, bạn chỉ có thể chia sẻ các biến giữa các mô-đun với module.exports hoặc bằng cách đặt var toàn cầu như trong trình duyệt, nhưng thay vì sử dụng cửa sổ bạn sử dụng global.your_var = value;.


2

Tôi đã kết thúc việc này bằng cách xuất một đối tượng đóng băng với các hàm getter ẩn danh, thay vì chính các hằng số. Điều này làm giảm nguy cơ lỗi khó chịu được giới thiệu do một lỗi đánh máy đơn giản của tên const, vì một lỗi thời gian chạy sẽ được đưa ra trong trường hợp lỗi chính tả. Dưới đây là một ví dụ đầy đủ cũng sử dụng Biểu tượng ES6 cho các hằng số, đảm bảo tính duy nhất và các hàm mũi tên ES6. Sẽ đánh giá cao thông tin phản hồi nếu bất cứ điều gì trong phương pháp này có vẻ có vấn đề.

'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

Tôi khuyên bạn nên làm điều đó với webpack (giả sử bạn đang sử dụng webpack).

Xác định các hằng số cũng đơn giản như cài đặt tệp cấu hình webpack:

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

Bằng cách này, bạn xác định chúng bên ngoài nguồn của mình và chúng sẽ có sẵn trong tất cả các tệp của bạn.


0

Tôi không nghĩ là một cách thực hành tốt để xâm chiếm không gian TOÀN CẦU từ các mô-đun, nhưng trong các tình huống có thể rất cần thiết để thực hiện nó:

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

Nó phải được coi là tác động của tài nguyên này. Nếu không đặt tên chính xác cho các hằng số đó, nguy cơ QUÁ NHIỀU đã được xác định các biến toàn cục, là điều có thật.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.