Perbedaan antara "module.exports" dan "ekspor" di CommonJs Module System

277

Pada halaman ini ( http://docs.nodejitsu.com/articles/getting-started/what-is-require ), ini menyatakan bahwa "Jika Anda ingin mengatur objek ekspor ke fungsi atau objek baru, Anda harus gunakan objek module.exports. "

Pertanyaan saya adalah mengapa.

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

Saya console.logged hasilnya ( result=require(example.js)) dan yang pertama adalah [Function]yang kedua {}.

Bisakah Anda jelaskan alasan di baliknya? Saya membaca posting di sini: module.exports vs ekspor di Node.js . Ini membantu, tetapi tidak menjelaskan alasan mengapa itu dirancang sedemikian rupa. Apakah akan ada masalah jika referensi ekspor dikembalikan secara langsung?

Xiao Peng - ZenUML.com
sumber
11
Selalu gunakan module.exports.
Gabriel Llamas
1
Saya pikir mengikuti saran yang disebutkan di atas memungkinkan untuk menghindari masalah ini.
Vitalii Korsakov
@GabrielLlamas jadi mengapa banyak paket hanya menggunakan exports, misalnya github.com/tj/consolidate.js/blob/master/lib/consolidate.js ?
CodyBugstein
3
@Imray Jika Anda selalu menggunakan module.exports, Anda tidak akan pernah salah, namun Anda dapat menggunakan exportsjika Anda tidak mengganti default diekspor objek, yaitu, jika Anda hanya melampirkan sifat seperti ini: var foo = require('foo').foo. fooProperti ini dapat diekspor seperti ini: exports.foo = ...dan tentu saja dengan module.exports. Ini pilihan pribadi, tetapi saya saat ini menggunakan module.exportsdan exportstepat.
Gabriel Llamas
Saya lebih suka exports.myFunc = function () {} jadi saya tidak harus memelihara daftar ekspor di bagian bawah file. Rasanya lebih dekat dengan praktik umum ekspor ketika Anda mendeklarasikan dalam ES6.
SacWebDeveloper

Jawaban:

626

moduleadalah objek JavaScript biasa dengan exportsproperti. exportsadalah variabel JavaScript biasa yang akan diatur ke module.exports. Di akhir file Anda, node.js pada dasarnya akan 'kembali' module.exportske requirefungsi. Cara yang disederhanakan untuk melihat file JS di Node bisa jadi ini:

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

// your code

return module.exports;

Jika Anda menetapkan properti pada exports, seperti exports.a = 9;, yang akan mengatur module.exports.ajuga karena benda-benda yang diedarkan sebagai referensi dalam JavaScript, yang berarti bahwa jika Anda menetapkan beberapa variabel ke objek yang sama, mereka adalah semua objek yang sama; jadi exportsdan itu module.exportsadalah objek yang sama.
Tetapi jika Anda mengatur exportske sesuatu yang baru, itu tidak akan lagi diatur ke module.exports, jadi exportsdan module.exportstidak lagi menjadi objek yang sama.

halte bus-goto
sumber
11
Benar, itu hanya dasar-dasar jenis referensi.
Vitalii Korsakov
18
Mengapa!? Mengapa orang dapat membaca ini hanya di sini. Ini harus menjadi tagline untuk setiap javaScript modular. Terima kasih
lima_fil
8
Dijelaskan dengan indah!
Aakash Verma
3
luar biasa, jawaban terbaik !!
John
5
Penjelasan yang bagus. Dokumentasi untuk module.exportsmenjelaskannya juga: nodejs.org/api/modules.html#modules_module_exports
Brian Morearty
52

Jawaban Renee dijelaskan dengan baik. Penambahan jawaban dengan contoh:

Node melakukan banyak hal pada file Anda dan salah satu yang penting adalah MEMBUNGKUS file Anda. Di dalam kode sumber nodejs "module.exports" dikembalikan. Mari kita mundur selangkah dan memahami bungkusnya. Misalkan Anda punya

salam.js

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

module.exports = greet;

kode di atas dibungkus sebagai IIFE (Ekspresi Fungsi Segera Dibatalkan) di dalam kode sumber nodejs sebagai berikut:

(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

dan fungsi di atas dipanggil (.apply ()) dan mengembalikan module.exports. Pada saat ini module.export dan ekspor menunjuk ke referensi yang sama.

Sekarang, bayangkan Anda menulis ulang greet.js sebagai

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

hasilnya akan

[Function]
{}

alasannya adalah: module.exports adalah objek kosong. Kami tidak menetapkan apa pun ke module.exports melainkan kami menetapkan ekspor = fungsi () ..... di greet.js baru. Jadi, module.exports kosong.

Ekspor dan module.exports secara teknis harus menunjuk ke referensi yang sama (itu benar !!). Tapi kami menggunakan "=" saat menetapkan function () .... ke ekspor, yang membuat objek lain di memori. Jadi, module.exports dan ekspor menghasilkan hasil yang berbeda. Dalam hal ekspor, kami tidak bisa menimpanya.

Sekarang, bayangkan Anda menulis ulang (ini disebut Mutation) greet.js (merujuk pada jawaban Renee) sebagai

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

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

hasilnya akan

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

Seperti yang Anda lihat module.exports dan ekspor menunjuk ke referensi yang sama yang merupakan fungsi. Jika Anda menyetel properti pada ekspor maka akan ditetapkan pada module.exports karena di JS, objek dilewatkan dengan referensi.

Kesimpulan selalu menggunakan module.exports untuk menghindari kebingungan. Semoga ini membantu. Selamat coding :)

Sdembla
sumber
Ini juga, jawaban yang indah dan melengkapi jawaban @ goto-bus-stop. :)
varun
23

Juga, satu hal yang dapat membantu untuk memahami:

math.js

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

client.js

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

Hebat, dalam hal ini:

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

Jadi, secara default, "ini" sebenarnya sama dengan module.exports.

Namun, jika Anda mengubah implementasi Anda menjadi:

math.js

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

module.exports = {
    add: add
};

Dalam hal ini, ini akan berfungsi dengan baik, "this" tidak sama dengan module.exports lagi, karena objek baru telah dibuat.

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

Dan sekarang, apa yang akan dikembalikan oleh yang diperlukan adalah apa yang didefinisikan di dalam module.exports, bukan ini atau ekspor, lagi.

Cara lain untuk melakukannya adalah:

math.js

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

Atau:

math.js

exports.add = function (a, b) {
    return a + b;
};
Rodrigo Branas
sumber
15

Jawaban Rene tentang hubungan antara exportsdan module.exportscukup jelas, itu semua tentang referensi javascript. Hanya ingin menambahkan itu:

Kami melihat ini di banyak modul simpul:

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

Ini akan memastikan bahwa meskipun kita mengubah module.exports, kita masih dapat menggunakan ekspor dengan membuat kedua variabel tersebut menunjuk ke objek yang sama.

fengshuo
sumber
Saya menjadi bingung dengan penjelasan ini, baik untuk menjelaskan?
GuyFreakz
6
@ GuyFreakz Saya tidak yakin apakah ini menyebabkan kebingungan Anda, tetapi module.exportsdan exportshanya variabel yang terpisah, diinisialisasi untuk referensi objek yang sama. Jika Anda mengubah referensi satu variabel apa, kedua variabel tidak lagi merujuk hal yang sama. Baris kode di atas memastikan kedua variabel diinisialisasi ke objek baru yang sama.
Andrew Palmer
Kasus penggunaan aktual yang dilewatkan oleh orang lain di @fengshuo. Terima kasih!
Aakash Verma
0

myTest.js

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

exports.put = function () {};

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

exportsdan module.exportssama dan referensi ke objek yang sama. Anda dapat menambahkan properti dengan kedua cara sesuai kenyamanan Anda.

Shashwat Gupta
sumber