module.exports กับการส่งออกเป็น Node.js


725

ฉันพบสัญญาต่อไปนี้ในโมดูล Node.js:

module.exports = exports = nano = function database_module(cfg) {...}

ผมสงสัยว่าคืออะไรแตกต่างกันmodule.exportsและexportsและเหตุผลที่ทั้งสองจะใช้ที่นี่


4
สำหรับลูกหลาน: nodejs.org/docs/latest/api/modules.html#module.exports
orftz

81
ทรัพยากรที่ดีเยี่ยม: hacksparrow.com/node-js-exports-vs-module-exports.html ^ _ ^
Naftali aka Neal

6
อัปเดตลิงก์ 'เพื่อลูกหลาน': nodejs.org/docs/latest/api/modules.html#modules_module_exports
Zeke

8
มันคือทั้งหมดที่เกี่ยวกับการอ้างอิง คิดว่าการส่งออกเช่นวัตถุตัวแปรท้องถิ่นชี้ไปที่ module.exports หากคุณเขียนทับค่าของการส่งออกคุณจะสูญเสียการอ้างอิงไปยัง module.exports และ module.exports เป็นสิ่งที่คุณเปิดเผยเป็นส่วนต่อประสานสาธารณะ
Gabriel Llamas

14
สรุปด่วน:ทั้งสองexportsและmodule.exportsชี้ไปที่วัตถุเดียวกันเว้นแต่คุณจะกำหนดใหม่ และในที่สุดmodule.exportsก็กลับมา ดังนั้นหากคุณกำหนดใหม่ให้exportsกับฟังก์ชันอย่าคาดหวังว่าฟังก์ชันจะไม่ถูกส่งคืน อย่างไรก็ตามถ้าคุณกำหนดฟังก์ชั่นแบบนี้exports.func = function...สิ่งที่ได้จะมีคุณสมบัติ func พร้อมฟังก์ชั่นเป็นค่า เนื่องจากคุณเพิ่มคุณสมบัติให้กับวัตถุที่exportsชี้ไปที่ ..
Muhammad Umer

คำตอบ:


426

การตั้งค่าmodule.exportsจะช่วยให้การทำงานที่จะเรียกว่าฟังก์ชั่นเช่นเมื่อdatabase_module requiredการตั้งค่าเพียงอย่างเดียวexportsไม่อนุญาตให้ส่งออกฟังก์ชั่นเนื่องจากโหนดส่งออกการmodule.exportsอ้างอิงวัตถุ รหัสต่อไปนี้จะไม่อนุญาตให้ผู้ใช้เรียกใช้ฟังก์ชัน

module.js

สิ่งต่อไปนี้ใช้ไม่ได้

exports = nano = function database_module(cfg) {return;}

ต่อไปนี้จะใช้งานได้หากmodule.exportsตั้งไว้

module.exports = exports = nano = function database_module(cfg) {return;}

ปลอบใจ

var func = require('./module.js');
// the following line will **work** with module.exports
func();

โดยทั่วไปnode.jsจะไม่เอ็กซ์พอร์ตอ็อบเจ็กต์ที่exportsอ้างอิงในปัจจุบัน แต่เอ็กซ์พอร์ตคุณสมบัติของสิ่งที่exportsอ้างอิงในตอนแรก แม้ว่าNode.jsจะทำการเอ็กซ์พอร์ตการmodule.exportsอ้างอิงวัตถุแต่อนุญาตให้คุณเรียกมันได้เหมือนฟังก์ชั่น


เหตุผลที่สำคัญอันดับสอง

พวกเขาตั้งค่าทั้งสองmodule.exportsและexportsเพื่อให้แน่ใจว่าexportsจะไม่อ้างอิงวัตถุที่ส่งออกก่อนหน้านี้ โดยการตั้งค่าทั้งที่คุณใช้exportsเป็นชวเลขและหลีกเลี่ยงข้อบกพร่องที่อาจเกิดขึ้นในภายหลังบนถนน

ใช้exports.prop = true แทนการmodule.exports.prop = trueบันทึกอักขระและหลีกเลี่ยงความสับสน


8
@ajostergaard: มันเกิดขึ้นเป็นชื่อของห้องสมุดที่ตัวอย่างของ OP ได้นำมา ในโมดูลจะอนุญาตให้ผู้เขียนเขียนสิ่งต่าง ๆnano.version = '3.3'แทนmodule.exports.version = '3.3'ซึ่งอ่านได้ชัดเจนขึ้นเล็กน้อย (โปรดทราบว่าnanoเป็นตัวแปรเฉพาะที่ประกาศเพียงเล็กน้อยก่อนที่การส่งออกโมดูลจะถูกตั้งค่า )
josh3736

3
@ มะนาว - ขอบคุณ - ฉันดีใจที่มันไม่เกี่ยวข้องส่วนใหญ่เพราะถ้ามันไม่ได้หมายความว่าฉันเข้าใจผิดทุกอย่าง : - | :)
ostergaard

เฮ้มะนาวนี่เป็นคำตอบที่ค่อนข้างเก่า แต่ฉันหวังว่าคุณจะสามารถอธิบายอะไรบางอย่างได้ หากฉันต้องตั้งค่าmodule.exportsแต่ไม่ใช่ exportsรหัสของฉันจะยังใช้งานได้หรือไม่ ขอบคุณสำหรับความช่วยเหลือใด ๆ !
ซาด Saeeduddin

1
@ Asad ใช่ฟังก์ชั่นจะส่งออกอย่างถูกต้องหากคุณตั้งค่าmodule.exports
Lime

@ เลียมขอบคุณสำหรับคำตอบที่มีค่า ข้อความค้นหาเพิ่มเติมอีกสองสามรายการ - ที่รายการของ server.js สิ่งที่คาดว่าจะเป็นค่าของโมดูลส่งออกและส่งออก เป็นโมดูลคาดว่าการส่งออกเป็นโมฆะและการส่งออกตั้งเป็นวัตถุที่ว่างเปล่า? เป็นมรดกหรือมีกรณีการใช้งานที่ถูกต้องเพื่อชี้การส่งออกและโมดูลส่งออกไปยังวัตถุที่แตกต่างกันสองที่เคย?
Sushil

504

แม้ว่าคำถามจะได้รับคำตอบและเป็นที่ยอมรับมานานแล้ว แต่ฉันต้องการแบ่งปัน 2 เซ็นต์ของฉัน:

คุณสามารถจินตนาการได้ว่าที่จุดเริ่มต้นไฟล์ของคุณมีบางสิ่งที่คล้ายกัน (สำหรับคำอธิบาย):

var module = new Module(...);
var exports = module.exports;

ป้อนคำอธิบายรูปภาพที่นี่

ดังนั้นสิ่งที่คุณต้องจำไว้เสมอว่าจะmodule.exportsไม่exportsถูกส่งกลับจากโมดูลของคุณเมื่อคุณต้องการโมดูลนั้นจากที่อื่น

ดังนั้นเมื่อคุณทำสิ่งที่ชอบ:

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

คุณกำลังเพิ่มฟังก์ชัน 2 ฟังก์ชันaและbไปยังวัตถุที่มีmodule.exportsคะแนนมากเกินไปด้วยดังนั้นtypeofผลลัพธ์ที่ส่งคืนจะเป็นobject:{ a: [Function], b: [Function] }

ของหลักสูตรนี้เป็นผลเดียวกันคุณจะได้รับถ้าคุณกำลังใช้ในตัวอย่างนี้แทนmodule.exportsexports

นี่เป็นกรณีที่คุณต้องการให้คุณmodule.exportsทำตัวเหมือนภาชนะที่มีค่าส่งออก ในขณะที่ถ้าคุณต้องการส่งออกฟังก์ชั่นคอนสตรัคเตอร์ก็มีบางอย่างที่คุณควรรู้เกี่ยวกับการใช้module.exportsหรือexports(โปรดจำอีกครั้งว่าmodule.exportsจะส่งคืนเมื่อคุณต้องการบางสิ่งไม่ใช่export)

module.exports = function Something() {
    console.log('bla bla');
}

ขณะนี้typeofผลลัพธ์ที่ส่งคืนคือ'function'และคุณสามารถกำหนดได้และเรียกใช้เช่น:
var x = require('./file1.js')();เนื่องจากคุณเขียนทับผลลัพธ์ที่ส่งคืนให้เป็นฟังก์ชัน

อย่างไรก็ตามการใช้งานexportsคุณไม่สามารถใช้สิ่งต่อไปนี้:

exports = function Something() {
    console.log('bla bla');
}
var x = require('./file1.js')(); //Error: require is not a function

เพราะด้วยexportsการอ้างอิงไม่ได้ชี้ไปที่วัตถุที่module.exportsมีจุดอีกต่อไปดังนั้นจึงไม่มีความสัมพันธ์ระหว่างexportsและmodule.exportsอีกต่อไป ในกรณีนี้module.exportsยังคงชี้ไปที่วัตถุที่ว่างเปล่า{}ซึ่งจะถูกส่งกลับ

คำตอบที่ได้รับการยอมรับจากหัวข้ออื่นควรช่วยด้วยเช่นกัน: Javascript ผ่านการอ้างอิงหรือไม่


2
คำอธิบายที่ดี แต่ฉันยังไม่เข้าใจว่าคุณสามารถตัดmodule.exportsออกจากโมดูลได้อย่างไรตัวอย่างเช่นในnpmแพ็คเกจนี้: github.com/tj/consolidate.js/blob/master/lib/consolidate.js
CodyBugstein

4
@ แสดงคำอธิบายที่นี่: JavaScript ผ่านการอ้างอิงหรือไม่ exports.a = function(){}; works, exports = function(){} doesn't work
cirpo

29
oooo ในที่สุดคำตอบนี้จะอธิบายมัน โดยทั่วไปการส่งออกหมายถึงวัตถุที่คุณสามารถเพิ่มคุณสมบัติ แต่ถ้าคุณกำหนดใหม่ให้ทำงานแล้วคุณจะไม่แนบคุณสมบัติกับวัตถุต้นฉบับนั้นอีกต่อไป ตอนนี้การส่งออกหมายถึงฟังก์ชั่นในขณะที่ module.exports ยังคงชี้ไปที่วัตถุนั้นและเนื่องจากมันเป็นสิ่งที่ส่งกลับ คุณสามารถพูดได้ว่าการส่งออกได้รับการเก็บขยะโดยทั่วไป
มูฮัมหมัดอูเมอร์

5
ดังนั้นสิ่งที่เป็นจุดของการใช้งานexports? ทำไมไม่ใช้ทุกครั้งmodule.exportsถ้ามันเป็นเพียงการมอบหมายตัวแปรใหม่? ดูเหมือนจะทำให้ฉันสับสน
jedd.ahyoung

1
@ jedd.ahyoung มันยุ่งยากน้อยกว่าที่จะเขียนexports.somethingแทนmodule.exports.something
Srle

209

โดยทั่วไปคำตอบนั้นอยู่ในสิ่งที่เกิดขึ้นจริงเมื่อจำเป็นต้องใช้โมดูลผ่านrequireคำสั่ง สมมติว่านี่เป็นครั้งแรกที่โมดูลจะถูกต้องการ

ตัวอย่างเช่น:

var x = require('file1.js');

เนื้อหาของ file1.js:

module.exports = '123';

เมื่อคำสั่งข้างต้นจะถูกดำเนินการModuleวัตถุจะถูกสร้างขึ้น มันสร้างฟังก์ชั่น:

function Module(id, parent) {
    this.id = id;
    this.exports = {};
    this.parent = parent;
    if (parent && parent.children) {
        parent.children.push(this);
    }

    this.filename = null;
    this.loaded = false;
    this.children = [];
}

exportsตามที่คุณเห็นวัตถุแต่ละโมดูลมีคุณสมบัติที่มีชื่อ requireนี่คือสิ่งที่จะถูกส่งกลับในที่สุดก็เป็นส่วนหนึ่งของ

ขั้นตอนต่อไปของความต้องการคือการห่อเนื้อหาของ file1.js ลงในฟังก์ชั่นที่ไม่ระบุชื่อเช่นด้านล่าง:

(function (exports, require, module, __filename, __dirname) { 
    //contents from file1.js
    module.exports = '123;
});

และฟังก์ชั่นที่ไม่ระบุชื่อนี้ถูกเรียกใช้ด้วยวิธีต่อไปนี้ที่moduleนี่หมายถึงModuleวัตถุที่สร้างขึ้นก่อนหน้านี้

(function (exports, require, module, __filename, __dirname) { 
    //contents from file1.js
    module.exports = '123;
}) (module.exports,require, module, "path_to_file1.js","directory of the file1.js");

ในฐานะที่เราสามารถมองเห็นภายในฟังก์ชั่นการโต้แย้งอย่างเป็นทางการหมายถึงexports module.exportsในสาระสำคัญมันเป็นความสะดวกสบายที่มีให้กับโปรแกรมเมอร์โมดูล

อย่างไรก็ตามความสะดวกนี้จำเป็นต้องใช้ความระมัดระวัง ในกรณีใด ๆ หากพยายามกำหนดวัตถุใหม่ให้กับการส่งออกให้แน่ใจว่าเราทำเช่นนี้

exports = module.exports = {};

ถ้าเราทำมันต่อไปนี้วิธีทางที่ผิด , module.exportsจะยังคงชี้ไปยังวัตถุที่สร้างขึ้นเป็นส่วนหนึ่งของโมดูล

exports = {};

เนื่องจากการเพิ่มสิ่งใด ๆ ลงในวัตถุส่งออกข้างต้นจะไม่มีผลกระทบต่อวัตถุ module.exports และจะไม่มีการส่งออกหรือส่งคืนเนื่องจากเป็นส่วนหนึ่งของความต้องการ


8
ลืมฉันที่นี่exports = module.exports = {};
Giant Elk

2
ฉันคิดว่านี่เป็นคำตอบที่ดีที่สุดมันอธิบายได้ว่าทำไมfunc()คำตอบของ @ William ไม่สำเร็จ
turtledove

2
ฉันไม่เห็นประโยชน์ใด ๆ ที่จะเพิ่ม exports = module.exports = app;ที่บรรทัดสุดท้ายของรหัส ดูเหมือนว่าmodule.exportsจะได้รับการส่งออกและเราจะไม่ใช้exportsเพราะอีกครั้งมันอยู่ที่บรรทัดสุดท้ายของรหัส ดังนั้นทำไมเราไม่เพียงแค่เพิ่มmodule.exports = app;
lvarayut

79

เริ่มแรกmodule.exports=exportsและrequireฟังก์ชันส่งคืนวัตถุที่module.exportsอ้างถึง

ถ้าเราเพิ่มคุณสมบัติให้กับวัตถุให้พูดexports.a=1แล้ว module.export และ export ยังคงอ้างถึงวัตถุเดียวกัน ดังนั้นถ้าเราเรียกต้องการและกำหนดโมดูลให้กับตัวแปรตัวแปรนั้นมีคุณสมบัติ a และค่าของมันคือ 1;

แต่ถ้าเราแทนที่หนึ่งในexports=function(){}นั้นพวกเขาจะแตกต่างกันในขณะนี้: การส่งออกหมายถึงวัตถุใหม่และโมดูลการส่งออกหมายถึงวัตถุต้นฉบับ และถ้าเราต้องการไฟล์มันจะไม่ส่งคืนวัตถุใหม่เนื่องจากโมดูลการส่งออกไม่ได้อ้างถึงวัตถุใหม่

สำหรับฉันฉันจะเพิ่มคุณสมบัติใหม่หรือแทนที่พวกเขาทั้งสองไปยังวัตถุใหม่ เพียงแค่แทนที่หนึ่งที่ไม่ถูกต้อง และโปรดจำไว้ว่าmodule.exportsเป็นเจ้านายที่แท้จริง


1
ใช่นี่คือคำตอบที่แท้จริง มันกระชับและชัดเจน คนอื่นอาจจะถูกต้อง แต่เต็มไปด้วยคำแฟนซีและไม่ได้เน้นไปที่คำตอบสำหรับคำถามนี้
Khoa

นี่คือคำตอบที่ชัดเจนที่สุด! ในกรณีที่คุณต้องการคั่นหน้านี่เป็นลิงก์ที่แม่นยำ: stackoverflow.com/questions/7137397/…
lambdarookie

56

exportsและmodule.exportsเหมือนกันเว้นแต่คุณจะมอบหมายexportsภายในโมดูลของคุณ

วิธีที่ง่ายที่สุดในการคิดเกี่ยวกับมันคือการคิดว่าบรรทัดนี้อยู่ด้านบนสุดของทุกโมดูล

var exports = module.exports = {};

หากภายในโมดูลของคุณคุณโอนแล้วคุณโอนภายในโมดูลและมันก็เท่ากับไม่ได้ของคุณexports module.exportsนี่คือเหตุผลถ้าคุณต้องการส่งออกฟังก์ชันคุณต้องทำ:

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

ถ้าคุณเพียงแค่ได้รับมอบหมายfunction() { ... }เพื่อexportsคุณจะได้รับการกำหนดใหม่จะไม่มีอีกต่อไปเพื่อจุดexportsmodule.exports

หากคุณไม่ต้องการอ้างถึงฟังก์ชั่นของคุณmodule.exportsทุกครั้งคุณสามารถทำได้:

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

ขอให้สังเกตว่าmodule.exportsเป็นข้อโต้แย้งที่เหลือมากที่สุด

การแนบคุณสมบัติกับexportsไม่เหมือนกันเนื่องจากคุณไม่ได้กำหนดใหม่ นั่นคือเหตุผลที่งานนี้

exports.foo = function() { ... }

9
นี่เป็นวิธีที่ง่ายที่สุดในการเข้าใจคำตอบทั้งหมด!
Adarsh ​​Konchady

2
ดีและตรงไปตรงมา
fibono

1
วิธีที่ง่ายและง่ายกว่าในการทำความเข้าใจคุณลักษณะนี้
FilipeCanatto

27

JavaScript ส่งผ่านวัตถุโดยคัดลอกข้อมูลอ้างอิง

มันมีความแตกต่างเล็กน้อยเกี่ยวกับวิธีที่วัตถุถูกส่งผ่านโดยการอ้างอิงใน JavaScript

exportsและmodule.exportsทั้งคู่ชี้ไปที่วัตถุเดียวกัน exportsเป็นตัวแปรและmodule.exportsเป็นคุณสมบัติของวัตถุโมดูล

สมมติว่าฉันเขียนบางสิ่งเช่นนี้:

exports = {a:1};
module.exports = {b:12};

exportsและmodule.exportsตอนนี้ชี้ไปที่วัตถุที่แตกต่าง การแก้ไขการเอ็กซ์พอร์ตจะไม่แก้ไข module.exports อีกต่อไป

เมื่อฟังก์ชั่นนำเข้าตรวจสอบmodule.exportsว่าได้รับแล้ว{b:12}


6
คำตอบที่ดีที่สุด!
นาย AJ

1
"JavaScript ผ่านการอ้างอิง" - ไม่
xehpuk

13

ฉันเพิ่งทำการทดสอบบางอย่างปรากฎว่าภายในรหัสโมดูลของ nodejs มันควรเป็นดังนี้:

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

ดังนั้น:

1:

exports = function(){}; // this will not work! as it make the exports to some other pointer
module.exports = function(){}; // it works! cause finally nodejs make the module.exports to export.

2:

exports.abc = function(){}; // works!
exports.efg = function(){}; // works!

3: แต่ในขณะที่ในกรณีนี้

module.exports = function(){}; // from now on we have to using module.exports to attach more stuff to exports.
module.exports.a = 'value a'; // works
exports.b = 'value b'; // the b will nerver be seen cause of the first line of code we have do it before (or later)

ลายแมนดังนั้นmodule.exportsคือการจัดเรียงของ 'จริง' จัดการโหนดที่จะไปออก แต่ในบางจุดที่คุณจะต้องเพิ่มของคุณทั้งหมดexportsจะmodule.exportsยกเว้นกรณีที่คุณกำลังใช้exports.namespace(กรณีที่ 2 ข้างต้น) ซึ่งในกรณีที่ดูเหมือนว่าจะเป็นเหมือน โหนดวิ่งextends(module.exports, exports);เพิ่มทุก namespaces ของexportsกับmodule.exportsวัตถุ? กล่าวอีกนัยหนึ่งถ้าคุณกำลังใช้อยู่exportsคุณอาจต้องการตั้งค่าคุณสมบัติหรือไม่
โคดี้

11

นี่คือคำอธิบายที่ดีเกี่ยวกับโมดูลโหนดในnode.js ในหนังสือการดำเนินการจากสิ่งพิมพ์Manning
ในที่สุดสิ่งที่ได้รับการส่งออกในใบสมัครของคุณคือmodule.exports
การส่งออก
ถูกตั้งค่าอย่างง่าย ๆ เป็นการอ้างอิงส่วนกลางไปยังmodule.exportsซึ่งเริ่มแรกถูกกำหนดเป็นวัตถุว่างเปล่าที่คุณสามารถเพิ่มคุณสมบัติได้ ดังนั้นexports.myFuncจดชวเลขเป็นเพียงสำหรับmodule.exports.myFunc

เป็นผลให้หากการส่งออกมีการตั้งค่ากับสิ่งอื่นก็แบ่งอ้างอิงระหว่าง module.exportsและการส่งออก เพราะโมดูลส่งออกเป็นสิ่งที่ได้รับการส่งออกจริง ๆ การส่งออกจะไม่ทำงานตามที่คาดไว้ - ไม่ได้อ้างอิงโมดูล. exportอีกต่อไป หากคุณต้องการรักษาการเชื่อมโยงนั้นคุณสามารถสร้างmodule.exports การอ้างอิงการส่งออกอีกครั้งดังนี้:

module.exports = exports = db;

8

ฉันผ่านการทดสอบบางอย่างและฉันคิดว่านี่อาจทำให้เกิดแสงสว่างในเรื่อง ...

app.js:

var ...
  , routes = require('./routes')
  ...;
...
console.log('@routes', routes);
...

รุ่นของ/routes/index.js:

exports = function fn(){}; // outputs "@routes {}"

exports.fn = function fn(){};  // outputs "@routes { fn: [Function: fn] }"

module.exports = function fn(){};  // outputs "@routes function fn(){}"

module.exports.fn = function fn(){};  // outputs "@routes { fn: [Function: fn] }"

ฉันยังเพิ่มไฟล์ใหม่:

./routes/index.js:

module.exports = require('./not-index.js');
module.exports = require('./user.js');

./routes/not-index.js:

exports = function fn(){};

./routes/user.js:

exports = function user(){};

เราได้ผลลัพธ์ "@routes {}"


./routes/index.js:

module.exports.fn = require('./not-index.js');
module.exports.user = require('./user.js');

./routes/not-index.js:

exports = function fn(){};

./routes/user.js:

exports = function user(){};

เราได้ผลลัพธ์ "@routes {fn: {}, ผู้ใช้: {}}"


./routes/index.js:

module.exports.fn = require('./not-index.js');
module.exports.user = require('./user.js');

./routes/not-index.js:

exports.fn = function fn(){};

./routes/user.js:

exports.user = function user(){};

เราได้รับผลลัพธ์ "@routes {ผู้ใช้: [ฟังก์ชั่น: ผู้ใช้]}" ถ้าเราเปลี่ยนuser.jsไป{ ThisLoadedLast: [Function: ThisLoadedLast] }เราจะได้รับผลลัพธ์ "@routes {ThisLoadedLast: [ฟังก์ชั่น: ThisLoadedLast]}"


แต่ถ้าเราปรับเปลี่ยน./routes/index.js...

./routes/index.js:

module.exports.fn = require('./not-index.js');
module.exports.ThisLoadedLast = require('./user.js');

./routes/not-index.js:

exports.fn = function fn(){};

./routes/user.js:

exports.ThisLoadedLast = function ThisLoadedLast(){};

... เราได้รับ "@routes {fn: {fn: [Function: fn]}, ThisLoadedLast: {ThisLoadedLast: [ฟังก์ชัน: ThisLoadedLast]}}"

ดังนั้นฉันขอแนะนำให้ใช้เสมอmodule.exportsในคำนิยามโมดูลของคุณ

ฉันไม่เข้าใจว่าเกิดอะไรขึ้นกับ Node ภายใน แต่โปรดแสดงความคิดเห็นหากคุณสามารถเข้าใจสิ่งนี้มากขึ้นเพราะฉันแน่ใจว่ามันช่วยได้

- การเข้ารหัสที่มีความสุข


ฉันคิดว่าพวกเขามีความซับซ้อนและสับสนโดยไม่จำเป็น ควรมีความโปร่งใสและใช้งานง่าย
ngungo

ฉันเห็นด้วย. มันอาจจะมีประโยชน์สำหรับ namespacing เป็นสถานการณ์บางอย่าง แต่โดยทั่วไปจะไม่สร้างหรือทำลายอะไร
โคดี

4

สิ่งนี้แสดงให้เห็นว่าการrequire()ทำงานในรูปแบบที่ง่ายที่สุดตัดตอนมาจากEloquent JavaScript

ปัญหา เป็นไปไม่ได้ที่โมดูลจะส่งออกค่าอื่นที่ไม่ใช่วัตถุส่งออกโดยตรงเช่นฟังก์ชั่น ตัวอย่างเช่นโมดูลอาจต้องการส่งออกเฉพาะตัวสร้างของประเภทวัตถุที่กำหนด ในตอนนี้มันไม่สามารถทำได้เพราะต้องการใช้exportsวัตถุที่สร้างเป็นค่าที่ส่งออกเสมอ

วิธีการแก้ปัญหา ให้กับโมดูลตัวแปรอื่นซึ่งเป็นวัตถุที่มีคุณสมบัติที่ใช้งานmodule exportsคุณสมบัตินี้เริ่มต้นที่วัตถุเปล่าที่สร้างโดยต้องการ แต่สามารถเขียนทับด้วยค่าอื่นเพื่อส่งออกอย่างอื่น

function require(name) {
  if (name in require.cache)
    return require.cache[name];
  var code = new Function("exports, module", readFile(name));
  var exports = {}, module = {exports: exports};
  code(exports, module);
  require.cache[name] = module.exports;
  return module.exports;
}
require.cache = Object.create(null);

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

4

นี่คือผลลัพธ์ของ

console.log("module:");
console.log(module);

console.log("exports:");
console.log(exports);

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

ป้อนคำอธิบายรูปภาพที่นี่

นอกจากนี้:

if(module.exports === exports){
    console.log("YES");
}else{
    console.log("NO");
}

//YES

หมายเหตุ: ข้อมูลจำเพาะ CommonJS อนุญาตให้ใช้ตัวแปรส่งออกเพื่อแสดงสมาชิกสาธารณะเท่านั้น ดังนั้นรูปแบบการส่งออกที่ระบุชื่อเป็นรูปแบบเดียวที่เข้ากันได้กับข้อมูลจำเพาะ CommonJS การใช้ module.exports เป็นส่วนขยายที่จัดทำโดย Node.js เพื่อสนับสนุนช่วงกว้างของรูปแบบการกำหนดโมดูล


4
var a = {},md={};

// ประการแรกการส่งออกและ module.exports ชี้วัตถุที่ว่างเปล่าเหมือนกัน

exp = a;//exports =a;
md.exp = a;//module.exports = a;

exp.attr = "change";

console.log(md.exp);//{attr:"change"}

// ถ้าคุณชี้ exp ไปที่อ็อบเจกต์อื่นแทนที่จะเป็น point มันจะเป็นคุณสมบัติของอ็อบเจกต์อื่น md.exp จะว่างเปล่า Object {}

var a ={},md={};
exp =a;
md.exp =a;

exp = function(){ console.log('Do nothing...'); };

console.log(md.exp); //{}

4

จากเอกสาร

ตัวแปรเอ็กซ์พอร์ตมีอยู่ในขอบเขตระดับไฟล์ของโมดูลและกำหนดค่าของ module.exports ก่อนที่โมดูลจะถูกประเมิน

อนุญาตทางลัดเพื่อให้ module.exports.f = ... สามารถเขียนได้อย่างรัดกุมยิ่งขึ้นเช่นเดียวกับ export.f = ....อย่างไรก็ตามโปรดทราบว่าเช่นเดียวกับตัวแปรใด ๆ หากมีการกำหนดค่าใหม่ให้กับการส่งออก ไม่ผูกพันกับโมดูลอีกต่อไปการส่งออก:

มันเป็นเพียงตัวแปรชี้ไปที่ module.exports


4

ฉันพบลิงค์นี้มีประโยชน์ในการตอบคำถามข้างต้น

http://timnew.me/blog/2012/04/20/exports-vs-module-exports-in-node-js/

เพื่อเพิ่มไปยังโพสต์อื่น ๆ ระบบโมดูลในโหนดทำ

var exports = module.exports 

ก่อนเรียกใช้งานโค้ดของคุณ ดังนั้นเมื่อคุณต้องการส่งออก = foo คุณอาจต้องการทำ module.exports = exports = foo แต่ใช้ exports.foo = foo ควรจะใช้ได้


ลิงค์ git เสีย
Jesse Hattabaugh

ลิงก์ได้รับการแก้ไขแล้ว
PawełGościcki

3

"ถ้าคุณต้องการให้รูทของการเอ็กซ์ปอร์ตโมดูลของคุณเป็นฟังก์ชั่น (เช่นคอนสตรัคเตอร์) หรือถ้าคุณต้องการเอ็กซ์ปอร์ตออบเจ็กต์ที่สมบูรณ์ในการมอบหมายครั้งเดียวแทนที่จะสร้างมันครั้งละหนึ่งคุณสมบัติ การส่งออก." - http://nodejs.org/api/modules.html


3

module.exportsและexportsทั้งสองชี้ไปที่วัตถุเดียวกันก่อนที่จะประเมินโมดูล

คุณสมบัติใด ๆ ที่คุณเพิ่มลงในmodule.exports วัตถุจะพร้อมใช้งานเมื่อโมดูลของคุณถูกใช้ในโมดูลอื่นโดยใช้requireคำสั่ง exportsเป็นทางลัดที่สร้างขึ้นสำหรับสิ่งเดียวกัน ตัวอย่างเช่น

module.exports.add = (a, b) => a+b

เทียบเท่ากับการเขียน:

exports.add = (a, b) => a+b

ดังนั้นมันก็โอเคตราบใดที่คุณไม่ได้กำหนดค่าใหม่ให้กับexportsตัวแปร เมื่อคุณทำสิ่งนี้:

exports = (a, b) => a+b 

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

หากคุณวางแผนที่จะกำหนดค่าใหม่ให้กับmodule.exportsแทนที่จะเพิ่มคุณสมบัติใหม่ให้กับวัตถุเริ่มต้นที่มีให้คุณควรพิจารณาทำตามที่ระบุด้านล่าง:

module.exports = exports = (a, b) => a+b

เว็บไซต์ Node.js มีคำอธิบายที่ดีมาก


2

1.export -> ใช้เป็นยูทิลิตี้ singleton
2. module-exports -> ใช้เป็นโลจิคัลอ็อบเจ็กต์เช่นเซอร์วิสโมเดลเป็นต้น


2

มาสร้างหนึ่งโมดูลด้วย 2 วิธี:

ทางเดียว

var aa = {
    a: () => {return 'a'},
    b: () => {return 'b'}
}

module.exports = aa;

วิธีที่สอง

exports.a = () => {return 'a';}
exports.b = () => {return 'b';}

และนี่คือวิธีที่ต้องใช้ ()จะรวมโมดูล

วิธีแรก:

function require(){
    module.exports = {};
    var exports = module.exports;

    var aa = {
        a: () => {return 'a'},
        b: () => {return 'b'}
    }
    module.exports = aa;

    return module.exports;
}

วิธีที่สอง

function require(){
    module.exports = {};
    var exports = module.exports;

    exports.a = () => {return 'a';}
    exports.b = () => {return 'b';}

    return module.exports;
}

2

เหตุใดจึงใช้ทั้งสองที่นี่

ฉันเชื่อว่าพวกเขาต้องการชัดเจนว่าmodule.exports, exportsและnanoชี้ไปที่ฟังก์ชั่นเดียวกัน - อนุญาตให้คุณใช้ตัวแปรเพื่อเรียกใช้ฟังก์ชันภายในไฟล์ nanoให้บริบทบางอย่างกับสิ่งที่ฟังก์ชั่นทำ

exportsจะไม่ถูกส่งออก ( module.exportsจะเท่านั้น) เหตุใดจึงต้องเขียนทับสิ่งนั้นด้วย

การแลกเปลี่ยนคำฟุ่มเฟื่อย จำกัด ความเสี่ยงของข้อบกพร่องในอนาคตเช่นการใช้exportsแทนmodule.exportsภายในไฟล์ นอกจากนี้ยังมีการชี้แจงว่าmodule.exportsและexportsในความเป็นจริงชี้ไปที่ค่าเดียวกัน


module.exports VS exports

ตราบใดที่คุณไม่มอบหมายmodule.exportsหรือexports(และเพิ่มคุณค่าให้กับวัตถุที่พวกเขาอ้างถึง) คุณจะไม่มีปัญหาใด ๆ และสามารถใช้อย่างปลอดภัยexportsให้กระชับยิ่งขึ้น

เมื่อกำหนดให้กับวัตถุที่ไม่ใช่วัตถุพวกเขากำลังชี้ไปยังสถานที่ต่าง ๆ ซึ่งอาจสร้างความสับสนเว้นแต่ว่าคุณต้องการโดยเจตนา module.exportsบางสิ่งบางอย่างโดยเฉพาะ (เช่นฟังก์ชั่น)

การตั้งค่าexportsเป็นวัตถุที่ไม่เหมาะสมไม่มากเท่าที่คุณจะต้องตั้งค่าmodule.exports = exportsในตอนท้ายเพื่อให้สามารถใช้มันในไฟล์อื่น ๆ

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

exports.msg = 'hi';
console.log(module.exports === exports); // true

exports = 'yo';
console.log(module.exports === exports); // false

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

module.exports = 'hello';
console.log(module.exports === exports); // false

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

ทำไมต้องมอบหมายmodule.exportsให้ฟังก์ชั่น?

รัดกุมยิ่งขึ้น! เปรียบเทียบว่าตัวอย่างที่ 2 สั้นกว่ามากเพียงใด:

helloWorld1.js: module.exports.hello = () => console.log('hello world');

app1.js: let sayHello = require('./helloWorld1'); sayHello.hello; // hello world

helloWorld2.js: module.exports = () => console.log('hello world');

app2.js: let sayHello = require('./helloWorld2'); sayHello; // hello world


2

ป้อนคำอธิบายรูปภาพที่นี่

แต่ละไฟล์ที่คุณสร้างเป็นโมดูล โมดูลเป็นวัตถุ มันมีคุณสมบัติที่เรียกว่าexports : {}ซึ่งเป็นวัตถุที่ว่างเปล่าโดยค่าเริ่มต้น

คุณสามารถสร้างฟังก์ชั่น / มิดเดิลแวร์และเพิ่มลงในวัตถุส่งออกที่ว่างเปล่าเช่นexports.findById() => { ... } นั้นrequireทุกที่ในแอปของคุณและใช้ ...

ควบคุม / user.js

exports.findById = () => {
    //  do something
}

จำเป็นต้องมีในroute.jsเพื่อใช้:

const {findyId} = './controllers/user'

2

เพื่อให้เข้าใจถึงความแตกต่างคุณต้องเข้าใจก่อนว่า Node.js ทำอะไรกับทุกโมดูลระหว่างรันไทม์ Node.js สร้างฟังก์ชั่น wrapper สำหรับทุกโมดูล:

 (function(exports, require, module, __filename, __dirname) {

 })()

ขอให้สังเกตพระรามแรกexportsเป็นวัตถุที่ว่างเปล่าและพระรามสามเป็นวัตถุที่มีคุณสมบัติหลายและเป็นหนึ่งในคุณสมบัติที่มีชื่อว่าmodule exportsนี่คือสิ่งที่exportsมาจากและสิ่งที่module.exportsมาจาก อันแรกคือวัตถุที่ผันแปรและอันหลังเป็นสมบัติของmoduleวัตถุ

ภายในโมดูล Node.js โดยอัตโนมัติไม่สิ่งนี้ที่จุดเริ่มต้น: module.exports = exportsและในที่สุดผลตอบแทนmodule.exports

ดังนั้นคุณจะเห็นว่าถ้าคุณโอนค่าบางอย่างเพื่อมันจะไม่ได้มีผลกระทบใดexportsmodule.exports(เพียงเพราะexportsชี้ไปที่วัตถุใหม่ แต่module.exportsยังคงเดิมexports)

let exports = {};
const module = {};
module.exports = exports;

exports = { a: 1 }
console.log(module.exports) // {}

แต่ถ้าคุณปรับปรุงคุณสมบัติของมันก็จะมีผลกระทบต่อexports module.exportsเพราะทั้งคู่ชี้ไปที่วัตถุเดียวกัน

let exports = {};
const module = {};
module.exports = exports;

exports.a = 1;
module.exports.b = 2;
console.log(module.exports) // { a: 1, b: 2 }

โปรดสังเกตด้วยว่าหากคุณกำหนดค่าอีกครั้งให้กับmodule.exportsดูเหมือนว่าไม่มีความหมายสำหรับexportsการอัปเดต การอัปเดตทุกรายการexportsจะถูกละเว้นเนื่องจากmodule.exportsชี้ไปที่วัตถุอื่น

let exports = {};
const module = {};
module.exports = exports;

exports.a = 1;
module.exports = {
  hello: () => console.log('hello')
}
console.log(module.exports) // { hello: () => console.log('hello')}

0

ในไฟล์ node js module.js ใช้เพื่อรัน module.load system.every ทุกครั้งเมื่อโหนดประมวลผลไฟล์ซึ่งจะตัดเนื้อหาไฟล์ js ของคุณดังต่อไปนี้

'(function (exports, require, module, __filename, __dirname) {',+
     //your js file content
 '\n});'

เนื่องจากการตัดคำนี้ในซอร์สโค้ด ur js คุณสามารถเข้าถึงการส่งออก, ต้องการ, โมดูล, ฯลฯ .. วิธีการนี้ใช้เพราะไม่มีวิธีอื่นในการรับฟังก์ชั่นที่เขียนไว้ในไฟล์ js ไปยังอีก

จากนั้นโหนดเรียกใช้ฟังก์ชันที่ถูกห่อหุ้มนี้โดยใช้ c ++ ในขณะนั้นการส่งออกวัตถุที่ผ่านเข้าไปในฟังก์ชั่นนี้จะถูกเติมเต็ม

คุณสามารถดูภายในฟังก์ชั่นการส่งออกพารามิเตอร์และโมดูล จริง ๆ แล้วการส่งออกเป็นสมาชิกสาธารณะของฟังก์ชันตัวสร้างโมดูล

ดูรหัสต่อไปนี้

คัดลอกรหัสนี้ลงใน b.js

console.log("module is "+Object.prototype.toString.call(module));
console.log("object.keys "+Object.keys(module));
console.log(module.exports);
console.log(exports === module.exports);
console.log("exports is "+Object.prototype.toString.call(exports));
console.log('----------------------------------------------');
var foo = require('a.js');
console.log("object.keys of foo: "+Object.keys(foo));
console.log('name is '+ foo);
foo();

คัดลอกรหัสนี้ไปยัง a.js

exports.name = 'hello';
module.exports.name = 'hi';
module.exports.age = 23;
module.exports = function(){console.log('function to module exports')};
//exports = function(){console.log('function to export');}

ตอนนี้รันโดยใช้โหนด

นี่คือผลลัพธ์

module is [object Object]
object.keys id,exports,parent,filename,loaded,children,paths
{}
true

การส่งออกคือ [วัตถุวัตถุ]

object.keys ของ foo: ชื่อคือ function () {console.log (ฟังก์ชั่นการส่งออกโมดูล ')} เพื่อส่งออกโมดูล

ตอนนี้ลบบรรทัดที่ถูกคอมเมนต์ใน a.js และใส่ความคิดเห็นบรรทัดด้านบนบรรทัดนั้นและลบบรรทัดสุดท้ายของ b.js และรัน

ในโลกจาวาสคริปต์คุณไม่สามารถมอบหมายวัตถุที่ส่งผ่านเป็นพารามิเตอร์ แต่คุณสามารถเปลี่ยนสมาชิกสาธารณะของฟังก์ชั่นเมื่อวัตถุของฟังก์ชั่นนั้นตั้งเป็นพารามิเตอร์ให้กับฟังก์ชั่นอื่น

อย่าลืม

ใช้ module.exports และเมื่อคุณต้องการรับฟังก์ชั่นเมื่อคุณต้องการใช้คีย์เวิร์ด ในตัวอย่างข้างต้นเรา var foo = require (a.js); คุณสามารถเห็นเราสามารถเรียก foo เป็นฟังก์ชัน

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


0
  1. ทั้งสองmodule.exportsและชี้ไปที่เดียวกันexportsfunction database_module(cfg) {...}

    1| var a, b;
    2| a = b = function() { console.log("Old"); };
    3|     b = function() { console.log("New"); };
    4|
    5| a(); // "Old"
    6| b(); // "New"

    คุณสามารถเปลี่ยนbบรรทัดที่ 3 aเป็นเอาท์พุทย้อนกลับ บทสรุปคือ:

    aและbมีความเป็นอิสระ

  2. ดังนั้นจึงmodule.exports = exports = nano = function database_module(cfg) {...}เทียบเท่ากับ:

    var f = function database_module(cfg) {...};
    module.exports = f;
    exports = f;

    สันนิษฐานข้างต้นซึ่งถูกต้องตามmodule.js foo.jsประโยชน์ของmodule.exports = exports = nano = function database_module(cfg) {...}ชัดเจนตอนนี้:

    • ในfoo.jsเนื่องจากmodule.exportsเป็นrequire('./module.js'):

      var output = require('./modules.js')();
    • ในmoduls.js: คุณสามารถใช้แทนexportsmodule.exports

ดังนั้นคุณจะมีความสุขถ้าทั้งคู่exportsและmodule.exportsชี้ไปที่สิ่งเดียวกัน

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