Node.js - ใช้ module.exports เป็นตัวสร้าง


120

ตามคู่มือ Node.js:

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

ตัวอย่างที่ให้คือ:

// file: square.js
module.exports = function(width) {
  return {
    area: function() {
      return width * width;
    }
  };
}

และใช้เช่นนี้:

var square = require('./square.js');
var mySquare = square(2);
console.log('The area of my square is ' + mySquare.area());

คำถามของฉัน: ทำไมตัวอย่างไม่ใช้สี่เหลี่ยมเป็นวัตถุ? ต่อไปนี้ถูกต้องหรือไม่และทำให้ตัวอย่างเป็น "เชิงวัตถุ" มากขึ้นหรือไม่

var Square = require('./square.js');
var mySquare = new Square(2);
console.log('The area of my square is ' + mySquare.area());

1
ตัวอย่างของคุณเป็นข้อผิดพลาดทางไวยากรณ์ หลังจากเปลี่ยนชื่อsquareเพื่ออยู่ไม่ได้ Squarenew square()
Sukima

3
ขออภัยนั่นเป็นการพิมพ์ผิด ซ่อมมัน. ความตั้งใจของฉันคือการแสดงชื่อวัตถุ / ฟังก์ชันโดยเริ่มต้นด้วยตัวพิมพ์ใหญ่และชื่ออินสแตนซ์ที่ขึ้นต้นด้วยตัวพิมพ์เล็ก
Naresh

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

คำตอบ:


173

โมดูล CommonJS อนุญาตให้กำหนดคุณสมบัติที่ส่งออกได้สองวิธี ไม่ว่าในกรณีใดคุณกำลังส่งคืน Object / Function เนื่องจากฟังก์ชันเป็นพลเมืองชั้นหนึ่งใน JavaScript จึงสามารถทำหน้าที่เหมือนกับ Objects ได้ (ในทางเทคนิคก็คือ Objects) ที่กล่าวว่าคำถามของคุณเกี่ยวกับการใช้newคำหลักมีคำตอบง่ายๆ: ใช่ ฉันจะอธิบาย ...

การส่งออกโมดูล

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

ตัวอย่างรหัสหลอกของการกำหนดโมดูล:

var theModule = {
  exports: {}
};

(function(module, exports, require) {

  // Your module code goes here

})(theModule, theModule.exports, theRequireFunction);

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

จำเป็นต้องมีตัวสร้าง

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

ดังนั้นสิ่งต่อไปนี้เป็นรหัสที่ดีอย่างสมบูรณ์และฉันขอแนะนำเป็นการส่วนตัว:

// My module
function MyObject(bar) {
  this.bar = bar;
}

MyObject.prototype.foo = function foo() {
  console.log(this.bar);
};

module.exports = MyObject;

// In another module:
var MyObjectOrSomeCleverName = require("./my_object.js");
var my_obj_instance = new MyObjectOrSomeCleverName("foobar");
my_obj_instance.foo(); // => "foobar"

ข้อกำหนดสำหรับผู้ที่ไม่ได้สร้าง

สิ่งเดียวกันสำหรับ non-constructor เช่นฟังก์ชัน:

// My Module
exports.someFunction = function someFunction(msg) {
  console.log(msg);
}

// In another module
var MyModule = require("./my_module.js");
MyModule.someFunction("foobar"); // => "foobar"

2
ฉันต้องการ ('./ my-object.js') ("foobar") สั้น ๆ ได้ไหม หรือไวยากรณ์ require ('module') (params) สำหรับกรณีการใช้งานอื่นหรือไม่?
Hampus Ahlgren

1
ไม่มีอะไรหยุดคุณได้ทั้งหมดเป็นเพียง JavaScript ใช่คุณสามารถใช้ไวยากรณ์ที่สั้นกว่านี้ได้
Sukima

3
ตัวอย่างรหัสหลอกของวิธีกำหนดโมดูลได้ล้างความเข้าใจของฉันเกี่ยวกับระบบโมดูล Node.js อย่างสมบูรณ์ ขอบคุณ!
Nitax

130

ในความคิดของฉันบางตัวอย่างของ node.js นั้นค่อนข้างมีการออกแบบ

คุณอาจคาดหวังว่าจะได้เห็นอะไรแบบนี้ในโลกแห่งความเป็นจริง

// square.js
function Square(width) {

  if (!(this instanceof Square)) {
    return new Square(width);
  }

  this.width = width;
};

Square.prototype.area = function area() {
  return Math.pow(this.width, 2);
};

module.exports = Square;

การใช้

var Square = require("./square");

// you can use `new` keyword
var s = new Square(5);
s.area(); // 25

// or you can skip it!
var s2 = Square(10);
s2.area(); // 100

สำหรับชาว ES6

class Square {
  constructor(width) {
    this.width = width;
  }
  area() {
    return Math.pow(this.width, 2);
  }
}

export default Square;

ใช้ใน ES6

import Square from "./square";
// ...

เมื่อใช้คลาสคุณต้องใช้newคีย์เวิร์ดเพื่อติดตั้ง ทุกสิ่งทุกอย่างยังคงเหมือนเดิม.


3
โครงสร้างรวบรัดผิดปกติ!
Christophe Marois

1
ดูเหมือนว่าในตัวอย่าง <ES6 ของคุณไม่มีความแตกต่างระหว่างการใช้newและไม่ใช้ แต่นั่นเป็นเพียงเพราะคุณมีการตรวจสอบนั้นthis instanceof squareหรือไม่? ถ้าเป็นเช่นนั้นกลไกนั้นทำอะไรกันแน่?
arichards

1
คำถามที่ฉันมีและค้นหาเผื่อว่าจะเป็นประโยชน์ต่อผู้อื่น: อยู่ที่ไหนimportและexportกำหนดไว้อย่างไร คำหลักเหล่านี้สงวนไว้ใน ECMAScript 6 (ES6) ก่อนหน้า ES6 ต้องใช้ไลบรารีเพื่อจัดการโมดูล การปรับเปลี่ยนโหนดของโหนดถูกจำลองตามโมดูลของไลบรารี CommonJS คืออะไรdefaultในexport default Square? สิ่งนี้ระบุสิ่งที่จะนำเข้าเมื่อคุณเพียงแค่นำเข้า 'ไฟล์' ไม่ใช่การเอ็กซ์พอร์ตเฉพาะจากไฟล์นั้น นานเท่าที่พวกเขามีอยู่ผมพบว่าหน้าเว็บเหล่านี้เป็นประโยชน์: spring.io/understanding/javascript-modulesและexploringjs.com/es6/ch_modules.html
arichards

1

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

สิ่งนี้จะเทียบเท่ากับ:

var square = function(width) {
  return {
    area: function() {
      return width * width;
    }
  };
}

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

สำหรับข้อโต้แย้งที่ซับซ้อนมากขึ้นnewให้ตรวจสอบสิ่งนี้: คำหลัก "ใหม่" ของ JavaScript ถือว่าเป็นอันตรายหรือไม่


3
ไม่มีอะไรผิดปกติกับการใช้คีย์เวิร์ดใหม่ ฉันเกลียด FUD ทั้งหมดที่อยู่รอบตัว
Sukima

1
@ สุกิมาเห็นด้วย. :-D ฉันกำลังชี้ให้เห็นว่าทำไมมันถึงไม่สำคัญในกรณีนี้และเชื่อมโยงกับคำถามอื่น ๆnewเพื่อให้คนอื่น ๆ สามารถเข้าร่วมในสงครามได้ที่นั่น
แบรด

0

โค้ดตัวอย่างคือ:

ในหลัก

square(width,function (data)
{
   console.log(data.squareVal);
});

การใช้สิ่งต่อไปนี้อาจได้ผล

exports.square = function(width,callback)
{
     var aa = new Object();
     callback(aa.squareVal = width * width);    
}

0

ในตอนท้าย Node เป็นเรื่องเกี่ยวกับ Javascript JS มีวิธีการหลายอย่างเพื่อบางสิ่งบางอย่างที่ประสบความสำเร็จเป็นสิ่งเดียวที่จะได้รับ "คอนสตรัค" สิ่งที่สำคัญคือการกลับฟังก์ชั่น

วิธีนี้คุณกำลังสร้างฟังก์ชันใหม่ตามที่เราสร้างขึ้นโดยใช้ JS บนสภาพแวดล้อมเว็บเบราว์เซอร์เป็นต้น

โดยส่วนตัวแล้วฉันชอบแนวทางต้นแบบตามที่ Sukima แนะนำในโพสต์นี้: Node.js - การใช้ module.exports เป็นตัวสร้าง

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