Sự khác biệt giữa mô-đun mô-đun.


276

Trên trang này ( http://docs.nodejitsu.com/articles/getting-started/what-is-require ), nó nói rằng "Nếu bạn muốn đặt đối tượng xuất khẩu thành một chức năng hoặc một đối tượng mới, bạn phải sử dụng đối tượng module.exports. "

Câu hỏi của tôi là tại sao.

// right
module.exports = function () {
  console.log("hello world")
}
// wrong
exports = function () {
  console.log("hello world")
}

Tôi console.logged kết quả ( result=require(example.js)) và cái đầu tiên là [Function]cái thứ hai {}.

Bạn có thể vui lòng giải thích lý do đằng sau nó? Tôi đọc bài viết ở đây: module.exports vs xuất khẩu trong Node.js . Nó rất hữu ích, nhưng không giải thích lý do tại sao nó được thiết kế theo cách đó. Sẽ có một vấn đề nếu tham chiếu xuất khẩu được trả lại trực tiếp?


11
Luôn luôn sử dụng module.exports.
Gabriel Llamas

1
Tôi nghĩ rằng những lời khuyên được đề cập ở trên cho phép để tránh vấn đề này.
Vitalii Korsakov

@GabrielLlamas vậy tại sao nhiều gói chỉ sử dụng exports, ví dụ github.com/tj/consolidate.js/blob/master/lib/consolidate.js ?
CodyBugstein

3
@Imray Nếu bạn luôn sử dụng module.exports, bạn sẽ không bao giờ sai, nhưng bạn có thể sử dụng exportsnếu bạn không thay thế đối tượng được xuất mặc định, nghĩa là, nếu bạn chỉ đính kèm các thuộc tính như thế này : var foo = require('foo').foo. Tài foosản này có thể được xuất khẩu như thế này: exports.foo = ...và tất nhiên cũng với module.exports. Đó là một lựa chọn cá nhân nhưng tôi hiện đang sử dụng module.exportsexportsthích hợp.
Gabriel Llamas

Tôi thích export.myFunc = function () {} vì vậy tôi không phải duy trì danh sách xuất khẩu ở cuối tệp. Nó cảm thấy gần gũi hơn với thực tiễn xuất khẩu phổ biến khi bạn khai báo trong ES6.
SacWebDeveloper

Câu trả lời:


624

modulelà một đối tượng JavaScript đơn giản với một thuộc exportstính. exportslà một biến JavaScript đơn giản được đặt thành module.exports. Vào cuối tập tin của bạn, Node.js sẽ cơ bản 'trở lại' module.exportsvới requirechức năng. Một cách đơn giản để xem tệp JS trong Node có thể là:

var module = { exports: {} };
var exports = module.exports;

// your code

return module.exports;

Nếu bạn đặt thuộc tính trên exports, như thế exports.a = 9;, điều đó cũng sẽ được đặt module.exports.avì các đối tượng được truyền xung quanh dưới dạng tham chiếu trong JavaScript, có nghĩa là nếu bạn đặt nhiều biến cho cùng một đối tượng, chúng đều là cùng một đối tượng; vì vậy sau đó exportsmodule.exportslà cùng một đối tượng.
Nhưng nếu bạn đặt exportsthành một cái gì đó mới, nó sẽ không còn được đặt thành module.exports, vì vậy exportsmodule.exportskhông còn là cùng một đối tượng.


11
Phải, đó chỉ là những điều cơ bản của các loại tham chiếu.
Vitalii Korsakov

18
Tại sao!? Tại sao người ta chỉ có thể đọc điều này ở đây. Đây phải là khẩu hiệu cho mọi javaScript mô-đun. Cảm ơn
lima_fil

8
Đẹp giải thích!
Aakash Verma

3
tuyệt vời, câu trả lời tốt nhất !!
Giăng

5
Giải thích tuyệt vời. Tài liệu để module.exportsmô tả nó cũng vậy: nodejs.org/api/modules.html#modules_module_exports
Brian Morearty

52

Câu trả lời của Renee cũng được giải thích. Ngoài ra với câu trả lời với một ví dụ:

Node thực hiện rất nhiều thứ cho tệp của bạn và một trong những điều quan trọng là VIẾT tệp của bạn. Bên trong mã nguồn của nodejs "module.exports" được trả về. Hãy lùi lại một bước và hiểu về trình bao bọc. Giả sử bạn có

chào mừng

var greet = function () {
   console.log('Hello World');
};

module.exports = greet;

đoạn mã trên được gói dưới dạng IIFE (Biểu thức hàm được gọi ngay lập tức) bên trong mã nguồn của nodejs như sau:

(function (exports, require, module, __filename, __dirname) { //add by node

      var greet = function () {
         console.log('Hello World');
      };

      module.exports = greet;

}).apply();                                                  //add by node

return module.exports;                                      //add by node

và hàm trên được gọi (.apply ()) và trả về module.exports. Tại thời điểm này module.exports và xuất khẩu chỉ đến cùng một tham chiếu.

Bây giờ, hãy tưởng tượng bạn viết lại hello.js như

exports = function () {
   console.log('Hello World');
};
console.log(exports);
console.log(module.exports);

đầu ra sẽ là

[Function]
{}

Lý do là: module.exports là một đối tượng trống. Chúng tôi đã không đặt bất cứ điều gì cho module.exports thay vào đó chúng tôi đặt export = function () ..... trong phần chào mới. Vì vậy, module.exports trống.

Về mặt kỹ thuật xuất khẩu và module.exports nên trỏ đến cùng một tham chiếu (điều đó đúng !!). Nhưng chúng ta sử dụng "=" khi gán hàm () .... để xuất, tạo ra một đối tượng khác trong bộ nhớ. Vì vậy, module.exports và xuất khẩu tạo ra kết quả khác nhau. Khi nói đến xuất khẩu, chúng ta không thể ghi đè lên nó.

Bây giờ, hãy tưởng tượng bạn viết lại (cái này được gọi là Đột biến) hello.js (tham khảo câu trả lời của Renee) là

exports.a = function() {
    console.log("Hello");
}

console.log(exports);
console.log(module.exports);

đầu ra sẽ là

{ a: [Function] }
{ a: [Function] }

Như bạn có thể thấy module.exports và export đang trỏ đến cùng một tham chiếu là một hàm. Nếu bạn đặt một thuộc tính khi xuất thì nó sẽ được đặt trên module.exports vì trong JS, các đối tượng được truyền bằng tham chiếu.

Kết luận là luôn luôn sử dụng module.exports để tránh nhầm lẫn. Hi vọng điêu nay co ich. Chúc mừng mã hóa :)


Đây cũng là một câu trả lời sâu sắc và bổ sung cho câu trả lời của @ goto-bus-stop. :)
varun

23

Ngoài ra, một điều có thể giúp hiểu:

math.js

this.add = function (a, b) {
    return a + b;
};

client.js

var math = require('./math');
console.log(math.add(2,2); // 4;

Tuyệt vời, trong trường hợp này:

console.log(this === module.exports); // true
console.log(this === exports); // true
console.log(module.exports === exports); // true

Do đó, theo mặc định, "cái này" thực sự bằng với module.exports.

Tuy nhiên, nếu bạn thay đổi triển khai thành:

math.js

var add = function (a, b) {
    return a + b;
};

module.exports = {
    add: add
};

Trong trường hợp này, nó sẽ hoạt động tốt, tuy nhiên, "cái này" không bằng với module.exports nữa, vì một đối tượng mới đã được tạo.

console.log(this === module.exports); // false
console.log(this === exports); // true
console.log(module.exports === exports); // false

Và bây giờ, những gì sẽ được trả về bởi yêu cầu là những gì đã được xác định bên trong module.exports, không phải cái này hay xuất khẩu nữa.

Một cách khác để làm điều đó sẽ là:

math.js

module.exports.add = function (a, b) {
    return a + b;
};

Hoặc là:

math.js

exports.add = function (a, b) {
    return a + b;
};

15

Câu trả lời của Rene về mối quan hệ giữa exportsmodule.exportskhá rõ ràng, đó là tất cả về các tài liệu tham khảo javascript. Chỉ muốn thêm rằng:

Chúng tôi thấy điều này trong nhiều mô-đun nút:

var app = exports = module.exports = {};

Điều này sẽ đảm bảo rằng ngay cả khi chúng tôi thay đổi module.exports, chúng tôi vẫn có thể sử dụng xuất khẩu bằng cách làm cho hai biến đó trỏ đến cùng một đối tượng.


Tôi trở nên bối rối với lời giải thích này, loại để giải thích?
GuyFreakz

6
@GuyFreakz Tôi không chắc điều này có nói lên sự nhầm lẫn của bạn không, module.exportsexportschỉ là các biến riêng biệt, được khởi tạo để tham chiếu cùng một đối tượng. Nếu bạn thay đổi những gì một tham chiếu biến, hai biến không còn tham chiếu cùng một điều. Dòng mã trên đảm bảo cả hai biến được khởi tạo cho cùng một đối tượng mới.
Andrew Palmer

Một trường hợp sử dụng thực tế mà mọi người khác đã bỏ lỡ trên @fengbury. Cảm ơn!
Aakash Verma

0

myTest.js

module.exports.get = function () {};

exports.put = function () {};

console.log(module.exports)
// output: { get: [Function], put: [Function] }

exportsmodule.exportsgiống nhau và là một tham chiếu đến cùng một đối tượng. Bạn có thể thêm thuộc tính bằng cả hai cách theo sự thuận tiện của bạn.

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.