Node.js - mewarisi dari EventEmitter

89

Saya melihat pola ini di beberapa perpustakaan Node.js:

Master.prototype.__proto__ = EventEmitter.prototype;

(sumber di sini )

Bisakah seseorang menjelaskan kepada saya dengan sebuah contoh, mengapa ini adalah pola yang umum dan kapan itu berguna?

jeffreyveon
sumber
Lihat pertanyaan ini untuk info stackoverflow.com/questions/5398487/…
Juicy Scripter
14
Catatan __proto__adalah anti-pola, silakan gunakanMaster.prototype = Object.create(EventEmitter.prototype);
Raynos
69
Sebenarnya, gunakanutil.inherits(Master, EventEmitter);
smart
1
@Raynos Apa itu anti-pola?
starbeamrainbowlabs
1
Ini sekarang lebih mudah dengan konstruktor ES6 Class. Periksa compat di sini: kangax.github.io/compat-table/es6 . Cek dokumen atau jawaban saya di bawah ini.
Breedly

Jawaban:

84

Seperti komentar di atas yang dikatakan kode itu, itu akan membuat Masterwarisan dari EventEmitter.prototype, sehingga Anda dapat menggunakan contoh dari 'kelas' itu untuk memancarkan dan mendengarkan acara.

Misalnya sekarang Anda dapat melakukan:

masterInstance = new Master();

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

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

Pembaruan : seperti yang ditunjukkan oleh banyak pengguna, cara 'standar' untuk melakukan itu di Node adalah menggunakan 'util.inherits':

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

Pembaruan ke-2 : dengan kelas ES6 pada kami, disarankan untuk memperluas EventEmitterkelas sekarang:

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();

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

myEmitter.emit('event');

Lihat https://nodejs.org/api/events.html#events_events

alessioalex.dll
sumber
4
(hanya sedikit pengingat yang harus dilakukan require('events').EventEmitterdulu-- Saya selalu lupa. Berikut tautan ke dokumen jika ada orang lain yang membutuhkannya: nodejs.org/api/events.html#events_class_events_eventemitter )
mikermcneil
3
BTW, konvensi untuk instance adalah huruf kecil pertama, jadi MasterInstanceseharusnya masterInstance.
khoomeister
Dan bagaimana Anda mempertahankan kemampuan untuk memeriksa apakah: masterInstance instanceof Master?
jayarjo
3
util.inheritsmelakukan hal yang buruk dengan memasukkan super_properti ke dalam Masterobjek. Ini tidak perlu dan mencoba memperlakukan warisan prototipe seperti warisan klasik. Lihat di bagian bawah halaman ini untuk penjelasan.
klh
2
@loretoparisi aja Master.prototype = EventEmitter.prototype;. Tidak perlu supers. Anda juga dapat menggunakan ES6 extends (dan didorong di dokumen Node.js on util.inherits) seperti ini class Master extends EventEmitter- Anda mendapatkan versi klasik super(), tetapi tanpa memasukkan apa pun ke dalamnya Master.
klh
81

Pewarisan Kelas Gaya ES6

Dokumen Node sekarang merekomendasikan penggunaan class inheritence untuk membuat event emitter Anda sendiri:

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {
  // Add any custom methods here
}

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

Catatan: Jika Anda mendefinisikan sebuah constructor()fungsi di MyEmitter, Anda harus memanggilnya super()untuk memastikan konstruktor kelas induk juga dipanggil, kecuali Anda punya alasan kuat untuk tidak melakukannya.

Breedly
sumber
9
Komentar ini tidak benar dan akhirnya menyesatkan dalam kasus ini. Calling super()yang tidak diperlukan , selama Anda tidak perlu / mendefinisikan konstruktor, maka jawaban asli Breedly (lihat sejarah EDIT) adalah sepenuhnya benar. Dalam hal ini Anda dapat menyalin dan menempel contoh yang sama ini di repl, menghapus konstruktor seluruhnya dan itu akan bekerja dengan cara yang sama. Itu adalah sintaks yang benar-benar valid.
Aurelio
39

Untuk mewarisi dari objek Javascript lain, EventEmitter Node.js secara khusus tetapi sebenarnya objek apa pun secara umum, Anda perlu melakukan dua hal:

  • menyediakan konstruktor untuk objek Anda, yang menginisialisasi objek sepenuhnya; jika Anda mewarisi dari beberapa objek lain, Anda mungkin ingin mendelegasikan beberapa pekerjaan inisialisasi ini ke konstruktor super.
  • menyediakan objek prototipe yang akan digunakan sebagai [[proto]]untuk objek yang dibuat dari konstruktor Anda; dalam hal Anda mewarisi dari beberapa objek lain, Anda mungkin ingin menggunakan instance dari objek lain sebagai prototipe Anda.

Ini lebih rumit di Javascript daripada yang terlihat di bahasa lain karena

  • Javascript memisahkan perilaku objek menjadi "konstruktor" dan "prototipe". Konsep ini dimaksudkan untuk digunakan bersama, tetapi dapat digunakan secara terpisah.
  • Javascript adalah bahasa yang sangat mudah dibentuk dan orang menggunakannya secara berbeda dan tidak ada definisi tunggal yang sebenarnya tentang apa arti "warisan".
  • Dalam banyak kasus, Anda dapat lolos dengan melakukan subset dari apa yang benar, dan Anda akan menemukan banyak contoh untuk diikuti (termasuk beberapa jawaban lain untuk pertanyaan SO ini) yang tampaknya berfungsi dengan baik untuk kasus Anda.

Untuk kasus spesifik EventEmitter Node.js, inilah yang berhasil:

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

Kelemahan yang mungkin:

  • Jika Anda menggunakan set prototipe untuk subclass Anda (Master.prototype), dengan atau tanpa menggunakan util.inherits, tetapi tidak memanggil super constructor ( EventEmitter) untuk instance kelas Anda, mereka tidak akan diinisialisasi dengan benar.
  • Jika Anda memanggil konstruktor super tetapi tidak menyetel prototipe, metode EventEmitter tidak akan berfungsi pada objek Anda
  • Anda bisa mencoba menggunakan instance yang diinisialisasi dari superclass ( new EventEmitter) sebagai Master.prototypeganti meminta konstruktor subclass Mastermemanggil super konstruktor EventEmitter; tergantung pada perilaku konstruktor superclass yang mungkin tampak berfungsi dengan baik untuk sementara waktu, tetapi bukan hal yang sama (dan tidak akan berfungsi untuk EventEmitter).
  • Anda bisa mencoba menggunakan super prototipe secara langsung ( Master.prototype = EventEmitter.prototype) daripada menambahkan lapisan objek tambahan melalui Object.create; ini mungkin tampak berfungsi dengan baik sampai seseorang mencocokkan objek Anda Masterdan secara tidak sengaja juga mencocokkan monkeypatch EventEmitterdan semua turunan lainnya. Setiap "kelas" harus memiliki prototipe sendiri.

Sekali lagi: untuk mewarisi dari EventEmitter (atau objek "kelas" yang sebenarnya), Anda ingin menentukan konstruktor yang terhubung ke konstruktor super dan menyediakan prototipe yang diturunkan dari super prototipe.

metamatt
sumber
19

Beginilah cara pewarisan prototipe (prototypal?) Dilakukan di JavaScript. Dari MDN :

Mengacu pada prototipe objek, yang mungkin berupa objek atau null (yang biasanya berarti objek tersebut adalah Object.prototype, yang tidak memiliki prototipe). Terkadang digunakan untuk mengimplementasikan pencarian properti berbasis prototipe-inheritance.

Ini bekerja juga:

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

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

Memahami JavaScript OOP adalah salah satu artikel terbaik yang saya baca akhir-akhir ini di OOP di ECMAScript 5.

Daff
sumber
7
Y.prototype = new X();adalah anti-pola, silakan gunakanY.prototype = Object.create(X.prototype);
Raynos
Ini bagus untuk diketahui. Bisakah saya membaca lebih banyak di suatu tempat? Akan tertarik bagaimana objek yang dihasilkan berbeda.
Daff
4
new X()membuat instance dari X.prototypedan menginisialisasinya dengan memanggilnya X. Object.create(X.prototype)hanya membuat contoh. Anda tidak ingin Emitter.prototypediinisialisasi. Saya tidak dapat menemukan artikel bagus yang menjelaskan hal ini.
Raynos
Ini masuk akal. Terima kasih telah menunjukkannya. Masih mencoba membiasakan diri di Node. Browser tidak ada di sana untuk ECMA5 (dan seperti yang saya pahami shim bukan yang paling dapat diandalkan).
Daff
1
Tautan lainnya juga rusak. Coba yang ini: robotlolita.github.io/2011/10/09/…
jlukanta
5

Saya pikir pendekatan dari http://www.bennadel.com/blog/2187-Extending-EventEmitter-To-Create-An-Evented-Cache-In-Node-js.htm ini cukup rapi:

function EventedObject(){

  // Super constructor
  EventEmitter.call( this );

  return( this );

}

Douglas Crockford juga memiliki beberapa pola pewarisan yang menarik: http://www.crockford.com/javascript/inheritance.html

Saya menemukan bahwa inheritence jarang dibutuhkan di JavaScript dan Node.js. Namun dalam menulis aplikasi yang pewarisannya dapat memengaruhi skalabilitas, saya akan mempertimbangkan performa yang dibandingkan dengan pemeliharaan. Jika tidak, saya hanya akan mendasarkan keputusan saya pada pola mana yang mengarah pada desain keseluruhan yang lebih baik, lebih mudah dirawat, dan tidak terlalu rentan terhadap kesalahan.

Uji pola yang berbeda di jsPerf, menggunakan Google Chrome (V8) untuk mendapatkan perbandingan kasar. V8 adalah mesin JavaScript yang digunakan oleh Node.js dan Chrome.

Berikut ini beberapa jsPerf untuk Anda mulai:

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

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

http://jsperf.com/inheritance-perf

wprl
sumber
1
Saya telah mencoba pendekatan ini dan keduanya emitdan onmuncul sebagai tidak terdefinisi.
dopatraman
Bukankah pengembalian (ini); hanya untuk dirantai?
blablabla
1

Untuk menambah respon wprl. Dia melewatkan bagian "prototipe":

function EventedObject(){

   // Super constructor
   EventEmitter.call(this);

   return this;

}
EventObject.prototype = new EventEmitter(); //<-- you're missing this part
guatedude2
sumber
1
Sebenarnya, Anda harus menggunakan Object.create daripada yang baru, jika tidak, Anda akan mendapatkan status instance serta perilaku pada prototipe seperti yang dijelaskan di tempat lain . Tetapi lebih baik menggunakan ES6 dan transpile atau util.inheritskarena banyak orang pintar akan terus memperbarui opsi tersebut untuk Anda.
Cool Blue