Node.js - sử dụng module.exports làm hàm tạo


120

Theo hướng dẫn sử dụng Node.js:

Nếu bạn muốn phần gốc của quá trình xuất mô-đun của mình là một hàm (chẳng hạn như hàm tạo) hoặc nếu bạn muốn xuất một đối tượng hoàn chỉnh trong một lần gán thay vì xây dựng nó một thuộc tính tại một thời điểm, hãy gán nó cho module.exports thay vì export .

Ví dụ được đưa ra là:

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

và được sử dụng như thế này:

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

Câu hỏi của tôi: tại sao ví dụ không sử dụng hình vuông làm đối tượng? Điều sau đây có hợp lệ không và nó có làm cho ví dụ trở nên "hướng đối tượng" hơn không?

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

1
Ví dụ của bạn là một lỗi cú pháp. Sau khi đổi tên squaređể Squarecác new square()không còn tồn tại.
Sukima 12/1213

3
Xin lỗi, đó là một lỗi đánh máy. Đã sửa nó. Ý định của tôi là hiển thị tên Đối tượng / Hàm bắt đầu bằng chữ hoa và tên cá thể bắt đầu bằng chữ thường.
Naresh

4
Tôi đã tìm hiểu rất nhiều, Đó là lý do tại sao tôi viết câu trả lời của mình theo cách tôi đã làm. Tôi chỉ muốn nói rằng tôi thực sự vui mừng khi những người khác nhìn mô-đun theo cách tương tự. Tôi thường sử dụng từ khóa mới và tổ chức các mô-đun của mình để xuất một hàm khởi tạo duy nhất. Tôi thấy nó làm cho khả năng đọc và khái niệm các giải pháp dễ dàng hơn. Tôi có thể biết ngay loại cấu trúc mà tôi định sử dụng. Kudo vì suy nghĩ giống tôi;)
Sukima 12/1213

Câu trả lời:


173

Các mô-đun CommonJS cho phép hai cách để xác định các thuộc tính đã xuất. Trong cả hai trường hợp, bạn đang trả về một Đối tượng / Hàm. Vì các hàm là công dân hạng nhất trong JavaScript nên chúng có thể hoạt động giống như Đối tượng (về mặt kỹ thuật chúng là Đối tượng). Điều đó cho thấy câu hỏi của bạn về việc sử dụng các newtừ khóa có một câu trả lời đơn giản: Có. Tôi sẽ minh họa ...

Xuất mô-đun

Bạn có thể sử dụng exportsbiến được cung cấp để đính kèm thuộc tính vào nó. Sau khi được yêu cầu trong một mô-đun khác, các thuộc tính gán đó sẽ có sẵn. Hoặc bạn có thể gán một đối tượng cho thuộc tính module.exports. Trong cả hai trường hợp, những gì được trả về require()là một tham chiếu đến giá trị của module.exports.

Ví dụ về mã giả về cách mô-đun được xác định:

var theModule = {
  exports: {}
};

(function(module, exports, require) {

  // Your module code goes here

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

Trong ví dụ trên module.exportsexportslà cùng một đối tượng. Phần thú vị là bạn không thấy bất kỳ điều gì trong số đó trong các mô-đun CommonJS của mình vì toàn bộ hệ thống sẽ chăm sóc điều đó cho bạn, tất cả những gì bạn cần biết là có một đối tượng mô-đun có thuộc tính xuất khẩu và biến xuất khẩu trỏ đến điều tương tự mà module.exports làm.

Yêu cầu với các hàm tạo

Vì bạn có thể đính kèm một hàm trực tiếp vào, module.exportsvề cơ bản bạn có thể trả về một hàm và giống như bất kỳ hàm nào, nó có thể được quản lý như một hàm tạo (Chữ in nghiêng vì sự khác biệt duy nhất giữa hàm và một hàm tạo trong JavaScript là cách bạn dự định sử dụng nó. Về mặt kỹ thuật không có sự khác biệt.)

Vì vậy, đoạn mã sau đây là hoàn toàn tốt và cá nhân tôi khuyến khích nó:

// 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"

Yêu cầu đối với các trình không xây dựng

Điều tương tự cũng xảy ra đối với các hàm không phải hàm tạo như:

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

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

2
Tôi có thể yêu cầu ('./ my-object.js') ("foobar") viết tắt không? Hay cú pháp request ('module') (params) cho một trường hợp sử dụng khác?
Hampus Ahlgren

1
Không có gì ngăn cản bạn, Tất cả chỉ là JavaScript. Vì vậy, có, bạn có thể sử dụng cú pháp ngắn hơn.
Sukima

3
Ví dụ mã giả về cách một mô-đun được xác định đã hoàn toàn làm sáng tỏ hiểu biết của tôi về hệ thống mô-đun Node.js. Cảm ơn bạn!
Nitax

130

Theo ý kiến ​​của tôi, một số ví dụ về node.js là khá giả tạo.

Bạn có thể mong đợi thấy một cái gì đó giống như thế này hơn trong thế giới thực

// 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;

Sử dụng

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

Đối với những người ES6

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

export default Square;

Sử dụng nó trong ES6

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

Khi sử dụng một lớp, bạn phải sử dụng newtừ khóa để cài đặt nó. Mọi thứ khác vẫn như cũ.


3
Cấu trúc ngắn gọn bất thường!
Christophe Marois

1
Vì vậy, có vẻ như trong ví dụ <ES6 của bạn, không có sự khác biệt giữa việc sử dụng newvà không sử dụng nó. Nhưng, đó chỉ là vì bạn có séc đó cho this instanceof square? Nếu vậy, chính xác thì cơ chế đó đang làm gì?
arichards

1
Các câu hỏi tôi đã có và tra cứu, trong trường hợp nó hữu ích cho người khác: Ở đâu importexportđược xác định? Đây là những từ khóa dành riêng trong ECMAScript 6 (ES6). Trước ES6, người ta phải sử dụng thư viện để quản lý các mô-đun. Mô-đun của Node được mô phỏng theo Mô-đun của thư viện CommonJS. Là gì defaulttrong export default Square? Điều này chỉ định những gì cần nhập khi bạn chỉ nhập 'tệp' chứ không phải các tệp xuất cụ thể khác từ tệp đó. Đối với miễn là họ tồn tại, tôi thấy các trang hữu ích: spring.io/understanding/javascript-modulesexploringjs.com/es6/ch_modules.html
arichards

1

Câu hỏi này không thực sự liên quan đến cách thức require()hoạt động. Về cơ bản, bất cứ điều gì bạn đặt module.exportsthành trong mô-đun của mình sẽ được trả về từrequire() gọi cho nó.

Điều này sẽ tương đương với:

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

Không cần newtừ khóa khi gọi square. Bạn không trả về bản thân hàm từsquare , bạn đang trả về một đối tượng mới ở cuối. Do đó, bạn có thể đơn giản gọi trực tiếp hàm này.

Để biết thêm các tranh luận phức tạp xung quanh new, hãy kiểm tra phần này: Từ khóa "mới" của JavaScript có bị coi là có hại không?


3
Không có gì sai khi sử dụng từ khóa mới. Tôi ghét tất cả FUD xung quanh nó.
Sukima 12/1213

1
@Sukima Đồng ý. :-D Tôi đang chỉ ra lý do tại sao nó không quan trọng trong trường hợp này, và liên kết với câu hỏi khác liên quan đến việc newđể những người khác có thể tham gia vào cuộc chiến ở đó.
Brad

0

Mã ví dụ là:

trong chính

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

sử dụng những điều sau đây có thể hoạt động

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

0

Cuối cùng, Node là về Javascript. JS có một số cách để hoàn thành một điều gì đó, cũng giống như cách lấy "hàm tạo", điều quan trọng là trả về một hàm .

Bằng cách này thực sự bạn đang tạo một hàm mới, ví dụ như chúng ta đã tạo bằng cách sử dụng JS trên môi trường Trình duyệt web.

Cá nhân tôi thích cách tiếp cận nguyên mẫu hơn, như Sukima đã đề xuất trên bài đăng này: Node.js - sử dụng module.exports làm hàm tạo

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.