Я бачу цей шаблон у багатьох бібліотеках Node.js:
Master.prototype.__proto__ = EventEmitter.prototype;
(джерело тут )
Хтось може пояснити мені на прикладі, чому це така поширена закономірність і коли це зручно?
Я бачу цей шаблон у багатьох бібліотеках Node.js:
Master.prototype.__proto__ = EventEmitter.prototype;
(джерело тут )
Хтось може пояснити мені на прикладі, чому це така поширена закономірність і коли це зручно?
__proto__
- це анти-шаблон, будь ласка, використовуйтеMaster.prototype = Object.create(EventEmitter.prototype);
util.inherits(Master, EventEmitter);
Відповіді:
Як зазначається у коментарі вище цього коду, він буде робити Master
наслідування з EventEmitter.prototype
, тому ви можете використовувати екземпляри цього "класу" для випуску та прослуховування подій.
Наприклад, тепер ви можете зробити:
masterInstance = new Master();
masterInstance.on('an_event', function () {
console.log('an event has happened');
});
// trigger the event
masterInstance.emit('an_event');
Оновлення : як зазначили багато користувачів, "стандартним" способом зробити це у Node буде використання "util.inherits":
var EventEmitter = require('events').EventEmitter;
util.inherits(Master, EventEmitter);
2-е оновлення : для класів ES6 рекомендується розширитиEventEmitter
клас зараз:
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
console.log('an event occurred!');
});
myEmitter.emit('event');
require('events').EventEmitter
спочатку - я завжди забуваю. Ось посилання на документи на випадок, якщо комусь це ще потрібно: nodejs.org/api/events.html#events_class_events_eventemitter )
MasterInstance
вводити першу літеру малими літерами, так і має бути masterInstance
.
Master.prototype = EventEmitter.prototype;
. Не потрібно суперерів. Ви також можете використовувати ES6-розширення (і це заохочується в документах Node.js util.inherits
) таким чином class Master extends EventEmitter
- ви отримуєте класичну форму super()
, але не вводячи нічого Master
.
Документи Node тепер рекомендують використовувати успадкування класів для створення власного випромінювача подій:
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');
Примітка: Якщо ви визначаєте constructor()
функцію в MyEmitter
, вам слід зателефонувати super()
з неї, щоб переконатися, що конструктор батьківського класу також викликається, якщо у вас немає вагомих причин цього не робити.
super()
буде не потрібно , до тих пір , поки ви не потрібні / визначити конструктор, тому оригінальну відповідь Breedly (див історії EDIT) був абсолютно правильним. У цьому випадку ви можете скопіювати та вставити цей самий приклад у repl, повністю видалити конструктор, і він буде працювати так само. Це цілком дійсний синтаксис.
Щоб успадкувати від іншого об'єкта Javascript, зокрема EventEmitter Node.js, але взагалі будь-якого об'єкта, вам потрібно зробити дві речі:
[[proto]]
об’єкт, створений із вашого конструктора; у випадку, якщо ви успадковуєте якийсь інший об'єкт, ви, ймовірно, хочете використовувати екземпляр іншого об'єкта як свій прототип.У Javascript це складніше, ніж може здатися іншими мовами, оскільки
Для конкретного випадку Node.js's EventEmitter, ось що працює:
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);
Можливі проблеми:
util.inherits
, але не викликаєте суперконструктор ( EventEmitter
) для екземплярів вашого класу, вони не будуть належним чином ініціалізовані.new EventEmitter
), Master.prototype
замість того, щоб конструктор підкласу Master
викликав суперконструктор EventEmitter
; залежно від поведінки конструктора суперкласу, який може здаватися, що якийсь час він працює нормально, але це не одне і те ж (і не буде працювати для EventEmitter).Master.prototype = EventEmitter.prototype
), замість того, щоб додавати додатковий шар об’єкта через Object.create; це може здатися, що він працює нормально, доки хтось не мавпує ваш об’єкт Master
і ненавмисно також мавпує EventEmitter
та всіх інших його нащадків. Кожен "клас" повинен мати свій прототип.Знову ж таки: для успадкування від EventEmitter (або насправді будь-якого існуючого "класу" об'єкта) ви хочете визначити конструктор, який прив'язує до суперконструктора і надає прототип, який походить від супер прототипу.
Так виконується прототипне (прототипове?) Успадкування в JavaScript. З MDN :
Посилається на прототип об'єкта, який може бути об'єктом або нулем (що зазвичай означає, що об'єктом є Object.prototype, який не має прототипу). Іноді його використовують для реалізації пошуку властивостей на основі успадкування прототипів.
Це також працює:
var Emitter = function(obj) {
this.obj = obj;
}
// DON'T Emitter.prototype = new require('events').EventEmitter();
Emitter.prototype = Object.create(require('events').EventEmitter.prototype);
Розуміння JavaScript ООП - одна з найкращих статей, які я останнім часом читав про ООП у ECMAScript 5.
Y.prototype = new X();
є анти-шаблоном, будь ласка, використовуйтеY.prototype = Object.create(X.prototype);
new X()
створює екземпляр екземпляра X.prototype
та ініціалізує його, викликаючи X
його. Object.create(X.prototype)
просто створює екземпляр екземпляра. Ви не Emitter.prototype
хочете бути ініціалізованим. Я не можу знайти хорошої статті, яка б це пояснювала.
Я вважав, що такий підхід з http://www.bennadel.com/blog/2187-Extending-EventEmitter-To-Create-An-Evented-Cache-In-Node-js.htm був досить акуратним:
function EventedObject(){
// Super constructor
EventEmitter.call( this );
return( this );
}
У Дугласа Крокфорда теж є кілька цікавих моделей успадкування: http://www.crockford.com/javascript/inheritance.html
Я вважаю, що успадкування рідше потрібне в JavaScript та Node.js. Але при написанні програми, де успадкування може вплинути на масштабованість, я вважав би продуктивність, зважену щодо ремонтопридатності. В іншому випадку я б базував своє рішення лише на тому, які зразки ведуть до кращого загального дизайну, є більш ремонтопридатними та менш схильними до помилок.
Перевірте різні шаблони в jsPerf, використовуючи Google Chrome (V8), щоб отримати приблизне порівняння. V8 - це механізм JavaScript, який використовується як Node.js, так і Chrome.
Ось декілька jsPerfs для початку:
http://jsperf.com/prototypes-vs-functions/4
emit
і on
виходять як невизначені.
Щоб додати відповідь wprl. Він пропустив частину "прототипу":
function EventedObject(){
// Super constructor
EventEmitter.call(this);
return this;
}
EventObject.prototype = new EventEmitter(); //<-- you're missing this part
util.inherits
оскільки багато розумних людей будуть постійно оновлювати ці варіанти для вас.