Câu hỏi Node.js - kế thừa từ EventEmitter


Tôi thấy mẫu này trong một vài thư viện Node.js:

Master.prototype.__proto__ = EventEmitter.prototype;

(nguồn đây)

Ai đó có thể xin giải thích cho tôi với một ví dụ, tại sao đây là một mô hình phổ biến và khi nó có ích?


76
2018-01-17 16:44


gốc


Tham khảo câu hỏi này để biết thông tin stackoverflow.com/questions/5398487/… - Juicy Scripter
chú thích __proto__ là một mẫu chống, vui lòng sử dụng Master.prototype = Object.create(EventEmitter.prototype); - Raynos
Trên thực tế, sử dụng util.inherits(Master, EventEmitter); - thesmart
@Raynos Mô hình chống là gì? - starbeamrainbowlabs
Điều này dễ dàng hơn với các nhà xây dựng ES6 Class. Kiểm tra compat tại đây: kangax.github.io/compat-table/es6 . Kiểm tra tài liệu hoặc câu trả lời của tôi bên dưới. - Breedly


Các câu trả lời:


Như nhận xét ở trên mã đó nói, nó sẽ làm cho Master Kế thừa từ EventEmitter.prototype, vì vậy bạn có thể sử dụng các trường hợp của 'lớp' đó để phát ra và nghe các sự kiện.

Ví dụ bạn có thể làm bây giờ:

masterInstance = new Master();

masterInstance.on('an_event', function () {
  console.log('an event has happened');
});

// trigger the event
masterInstance.emit('an_event');

Cập nhật: như nhiều người dùng đã chỉ ra, cách 'chuẩn' làm điều đó trong Node sẽ là sử dụng 'util.inherits':

var EventEmitter = require('events').EventEmitter;
util.inherits(Master, EventEmitter);

77
2018-01-17 16:54



(chỉ một chút nhắc nhở để làm require('events').EventEmitter đầu tiên-- tôi luôn quên. Đây là liên kết đến tài liệu trong trường hợp bất kỳ ai khác cần đến tài liệu đó: nodejs.org/api/events.html#events_class_events_eventemitter) - mikermcneil
BTW, quy ước cho các trường hợp là viết hoa chữ cái đầu tiên, vì vậy MasterInstance nên là masterInstance. - khoomeister
Và làm thế nào để bạn duy trì khả năng kiểm tra nếu: masterInstance instanceof Master? - jayarjo
util.inherits làm một điều khó chịu khi tiêm super_ tài sản vào Master vật. Đó là không cần thiết và cố gắng để điều trị thừa kế nguyên mẫu như thừa kế cổ điển. Hãy nhìn vào đáy của điều này trang để giải thích. - Killah
@loretoparisi Master.prototype = EventEmitter.prototype;. Không cần cho supers. Bạn cũng có thể sử dụng ES6 mở rộng (và nó được khuyến khích trong tài liệu Node.js trên util.inherits) như thế này class Master extends EventEmitter - bạn có cổ điển super()nhưng không tiêm bất cứ thứ gì vào Master. - Killah


Thừa kế lớp ES 6

Những điều này xuất phát trực tiếp từ các tài liệu, nhưng tôi nghĩ sẽ tốt hơn nếu thêm chúng vào câu hỏi phổ biến này cho bất kỳ ai đang tìm kiếm.

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {
  constructor() {
    super(); //must call super for "this" to be defined.
  }     
}

const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
  console.log('an event occurred!');
});
myEmitter.emit('event');

Tôi muôn git thank bất cứ ai thêm vào đó. Event Emitter.

Chú thích: Tài liệu không gọi super() trong hàm tạo sẽ gây ra this không được xác định. Xem này vấn đề.


56
2018-03-11 16:22



`` `lớp MyEmitter mở rộng EventEmitter {constructor () {super (); // phải gọi super cho "this" được định nghĩa. }} `` ` - Chase Wilson
Imo câu trả lời hay nhất, nhưng có một sai lầm: super() phải ở hàm tạo. Hãy sửa chữa nó. - Firanolfind
Những nhận xét này không chính xác và kết thúc là gây hiểu lầm trong trường hợp này. Đang gọi super() Là không yêu cầu, miễn là bạn không cần / định nghĩa một hàm tạo, do đó câu trả lời gốc của Breedly (xem lịch sử EDIT) là hoàn toàn chính xác. Trong trường hợp này, bạn có thể sao chép và dán ví dụ này rất giống nhau trong repl, loại bỏ hàm khởi tạo hoàn toàn và nó sẽ làm việc theo cùng một cách. Đó là cú pháp hoàn toàn hợp lệ. - Nobita


Để kế thừa từ một đối tượng Javascript khác, EventEmitter của Node.js nói riêng nhưng thực sự là bất kỳ đối tượng nào nói chung, bạn cần phải làm hai việc:

  • cung cấp một hàm tạo cho đối tượng của bạn, nó khởi tạo hoàn toàn đối tượng; trong trường hợp bạn đang kế thừa từ một số đối tượng khác, bạn có thể muốn ủy nhiệm một số công việc khởi tạo này cho siêu khởi tạo.
  • cung cấp một đối tượng nguyên mẫu sẽ được sử dụng làm [[proto]] cho các đối tượng được tạo từ hàm tạo của bạn; trong trường hợp bạn đang kế thừa từ một số đối tượng khác, bạn có thể muốn sử dụng một cá thể của đối tượng khác làm nguyên mẫu của bạn.

Điều này phức tạp hơn trong Javascript so với nó có vẻ như trong các ngôn ngữ khác bởi vì

  • Javascript phân tách hành vi đối tượng thành "hàm tạo" và "nguyên mẫu". Những khái niệm này có nghĩa là để được sử dụng với nhau, nhưng có thể được sử dụng một cách riêng biệt.
  • Javascript là một ngôn ngữ rất dễ đọc và mọi người sử dụng nó một cách khác nhau và không có định nghĩa duy nhất nào về "kế thừa" nghĩa là gì.
  • Trong nhiều trường hợp, bạn có thể lấy đi một tập con của những gì là đúng, và bạn sẽ tìm thấy rất nhiều ví dụ để theo dõi (bao gồm một số câu trả lời cho câu hỏi SO này) có vẻ tốt cho trường hợp của bạn.

Đối với trường hợp cụ thể của EventEmitter của Node.js, đây là những gì hoạt động:

var EventEmitter = require('events').EventEmitter;
var util = require('util');

// Define the constructor for your derived "class"
function Master(arg1, arg2) {
   // call the super constructor to initialize `this`
   EventEmitter.call(this);
   // your own initialization of `this` follows here
};

// Declare that your class should use EventEmitter as its prototype.
// This is roughly equivalent to: Master.prototype = Object.create(EventEmitter.prototype)
util.inherits(Master, EventEmitter);

Có thể foibles:

  • Nếu bạn sử dụng thiết lập nguyên mẫu cho lớp con của bạn (Master.prototype), có hoặc không sử dụng util.inherits, nhưng đừng gọi nhà xây dựng siêu (EventEmitter) cho các trường hợp của lớp học của bạn, chúng sẽ không được khởi tạo đúng cách.
  • Nếu bạn gọi hàm dựng siêu nhưng không đặt nguyên mẫu, các phương thức EventEmitter sẽ không hoạt động trên đối tượng của bạn
  • Bạn có thể thử sử dụng một thể hiện khởi tạo của siêu lớp (new EventEmitter) như Master.prototype thay vì có hàm tạo lớp con Master gọi nhà xây dựng siêu EventEmitter; tùy thuộc vào hành vi của hàm tạo siêu lớp có vẻ như nó hoạt động tốt trong một thời gian, nhưng không phải là điều tương tự (và sẽ không hoạt động đối với EventEmitter).
  • Bạn có thể cố gắng sử dụng siêu mẫu trực tiếp (Master.prototype = EventEmitter.prototype) thay vì thêm một lớp đối tượng bổ sung qua Object.create; điều này có vẻ như nó làm việc tốt cho đến khi ai đó monkeypatches đối tượng của bạn Master và vô tình cũng đã bắt cóc EventEmitter và tất cả các hậu duệ khác của nó. Mỗi "lớp" nên có nguyên mẫu riêng của nó.

Một lần nữa: để kế thừa từ EventEmitter (hoặc thực sự là bất kỳ đối tượng hiện có nào "class"), bạn muốn định nghĩa một constructor mà chain cho super constructor và cung cấp một prototype có nguồn gốc từ super prototype.


36
2018-05-28 17:55





Đây là cách thức thừa kế nguyên mẫu (prototypal?) Được thực hiện bằng JavaScript. Từ MDN:

Đề cập đến nguyên mẫu của đối tượng, có thể là một đối tượng hoặc null   (thường có nghĩa là đối tượng là Object.prototype, không có   nguyên mẫu). Nó đôi khi được sử dụng để thực hiện thừa kế nguyên mẫu   tra cứu thuộc tính dựa trên.

Điều này cũng hoạt động:

var Emitter = function(obj) {
    this.obj = obj;
}

// DON'T Emitter.prototype = new require('events').EventEmitter();
Emitter.prototype = Object.create(require('events').EventEmitter.prototype);

Hiểu về OOP JavaScript là một trong những bài viết hay nhất tôi đọc gần đây trên OOP trong ECMAScript 5.


19
2018-01-17 16:55



Y.prototype = new X(); là một mẫu chống, vui lòng sử dụng Y.prototype = Object.create(X.prototype); - Raynos
Điều này là tốt để biết. Tôi có thể đọc thêm ở đâu đó không? Sẽ được quan tâm như thế nào các đối tượng kết quả là khác nhau. - Daff
new X() khởi tạo một thể hiện của X.prototype và khởi tạo nó bằng cách gọi X trên đó. Object.create(X.prototype) chỉ cần khởi tạo một thể hiện. Bạn không wnat Emitter.prototype để được khởi tạo. Tôi không thể tìm thấy một bài viết hay giải thích điều này. - Raynos
Điều này thật ý nghĩa. Cảm ơn đã chỉ ra điều đó. Vẫn cố gắng để có được những thói quen tốt trên Node. Các trình duyệt chỉ không có ở đó cho ECMA5 (và như tôi đã hiểu shim không phải là đáng tin cậy nhất). - Daff
Liên kết khác cũng bị hỏng. Hãy thử cái này: robotlolita.github.io/2011/10/09/… - jlukanta


Tôi nghĩ cách tiếp cận này từ http://www.bennadel.com/blog/2187-Extending-EventEmitter-To-Create-An-Evented-Cache-In-Node-js.htm khá gọn gàng:

function EventedObject(){

  // Super constructor
  EventEmitter.call( this );

  return( this );

}

Douglas Crockford cũng có một số mẫu kế thừa thú vị: http://www.crockford.com/javascript/inheritance.html

Tôi thấy tính kế thừa thường ít cần thiết hơn trong JavaScript và Node.js. Nhưng bằng văn bản một ứng dụng mà sự thừa kế có thể ảnh hưởng đến khả năng mở rộng, tôi sẽ xem xét hiệu suất cân nhắc chống lại khả năng bảo trì. Nếu không, tôi sẽ chỉ căn cứ quyết định của tôi về những mẫu nào dẫn đến thiết kế tổng thể tốt hơn, dễ bảo trì hơn và ít bị lỗi hơn.

Kiểm tra các mẫu khác nhau trong jsPerf, sử dụng Google Chrome (V8) để so sánh thô. V8 là công cụ JavaScript được sử dụng bởi cả Node.js và Chrome.

Dưới đây là một số jsPerfs để giúp bạn bắt đầu:

http://jsperf.com/prototypes-vs-functions/4

http://jsperf.com/inheritance-proto-vs-object-create

http://jsperf.com/inheritance-perf


5
2018-03-01 16:50



Tôi đã thử phương pháp này và cả hai emit và on đang đến như không xác định. - dopatraman
Không phải là sự trở lại (điều này); chỉ cho chuỗi? - blablabla


Để thêm vào phản hồi của wprl. Ông đã bỏ lỡ phần "nguyên mẫu":

function EventedObject(){

   // Super constructor
   EventEmitter.call(this);

   return this;

}
EventObject.prototype = new EventEmitter(); //<-- you're missing this part

1
2018-06-03 20:47



Trên thực tế, bạn nên sử dụng Object.create thay vì mới nếu không bạn sẽ có được trạng thái instance cũng như hành vi trên nguyên mẫu như đã giải thích nơi khác. Nhưng tốt hơn để sử dụng ES6 và transpile hoặc util.inherits vì rất nhiều người thông minh sẽ giữ cho các tùy chọn này được cập nhật cho bạn. - Cool Blue