I have a child class which extends parent class. I also want child class to be able to emit events. Examples I found were extending EventEmitter. But JS does not allow multiple inheritance. I tried to implement it via mixin but not sure how to do without converting my parent class to a mixin.
Example
Say there is a base class for vehicles
all vehicles move and some can emit events when they move
class Vehicle {
constructor(name) { this.name = name }
move() { console.log(this.name, 'Moved'); }
}
class Car extends Vehicle {
constructor() { super('Ford'); }
move() {
super.move();
this.engineSound();
}
engineSound() {
console.log("Vroooom!");
}
// some other car related properties
}
class Ambulance extends Vehicle { // want to extend EventEmitter aswell
constructor() { super('Ambulance1'); }
move() {
super.move();
// emit moved event here
}
}
let normalVehicle = new Vehicle('Normal');
normalVehicle.move();
let car = new Car();
car.move();
let ambulance = new Ambulance();
// Listen for moving ambulance here
// ambulance.on('moved', () => { console.log('Ambulance coming') });
ambulance.move();
But to emit events docs suggest extending EventEmitter but JS does not support extending multiple classes.
Saw few examples which were using Object.assign but not sure how that can be applied here.
es6 class syntax doesn't support multiple inheritance at all. You can try weird trickery if you convert all classes to es5 constructor functions and then use things like Object.assign. But the best approach would be to use composition over inheritance.
OOP is used to try and mimic real world objects, so in real world terms it also doesn't make sense for an ambulance to be itself an event emitter.
However, using a composition approach, it makes sense for an ambulance to have an event emitter, like a radio or other such transmission device:
class Ambulance extends Vehicle {
#radio = new EventEmitter();
constructor() {
super("Ambulance");
}
emit(event) { this.#radio.emit(event) }
on(event, handler) { this.#radio.on(event, handler) }
move() {
super.move();
this.emit("moved");
}
}
let ambulance = new Ambulance();
ambulance.on('moved', () => { console.log('Ambulance coming') });
ambulance.move();
composition example
Note that the radio is private (using the new JS private fields feature), and I only supply a facade for the exact functionality I want the ambulance users to have, in this case 'on' and 'emit'.
With that said...
Lets get crazy and actually answer your question! Again, this will require to use es5 constructor functions:
// Base class - no inheritance
var Vehicle = (function () {
function Vehicle(name) {
this.name = name;
}
Vehicle.prototype.move = function () {
return this.name + " Moved\n";
};
return Vehicle;
})();
// Derived from Vehicle - regular inheritance from a single object
var Car = (function (_super) {
function Car() {
// Calling super() in constructor
_super.call(this, "Ford");
}
// extend Vehicle
Car.prototype = Object.create(Vehicle.prototype);
// bring back Car's constructor, because last step overrid it
Object.defineProperty(Car.prototype, "constructor", {
value: Car,
enumerable: false,
writable: true,
configurable: true
});
Car.prototype.move = function () {
return _super.prototype.move.call(this) + this.engineSound();
};
Car.prototype.engineSound = function () {
return "Vroooom!\n";
};
return Car;
})(Vehicle);
// Derived from Vehicle and EventEmitter - multiple inheritance!
var Ambulance = (function (_superVehicle, _superEventEmitter) {
function Ambulance() {
_superVehicle.call(this, "Ambulance");
_superEventEmitter.call(this);
}
// Create joint prototype using Object.assign
var jointPrototype = Object.assign(
{},
_superEventEmitter.prototype,
_superVehicle.prototype
);
// Connecting Ambulance to prototype chain
Ambulance.prototype = Object.create(jointPrototype);
// restore Ambulance's constructor
Object.defineProperty(Ambulance.prototype, "constructor", {
value: Ambulance,
enumerable: false,
writable: true,
configurable: true
});
Ambulance.prototype.move = function () {
this.emit("moved");
return _superVehicle.prototype.move.call(this);
};
return Ambulance;
})(Vehicle, EventEmitter);
es5 multiple inheritance example
Related
How can i use class instance in another class like a pointer in C++ to class instance functions?
Example:
class A {
constructor()
{
this.block = [];
}
method()
{
return this.blocks.length;
}
}
another class:
class B {
constructor(instance)
{
this.instance = instance;
}
method()
{
this.instance.method(); // here i'm getting cannot get length of undefined
}
}
If i'm trying to to like that i'm getting problems to call it
You can try this. Here, when creating B class's instance I give into it an A class's instance as argument. Then inside B we can call A instance's methods, and access its properties.
Also, as #ViaTech posted you can use static methods to access them without needing to initialize an object of the class. That is what static methods is. Refer Static Methods
class B {
constructor(instance)
{
this.instance = instance;
}
method()
{
this.instance.method();
}
}
class A {
constructor()
{
}
method()
{
console.log("A's method");
}
}
var a = new A();
var b = new B(a);
b.method(); // A's method
You can easily do this in JS by calling a static method like so:
class A {
static write(){ //static method
console.log("Called write from A");
}
}
class B {
doIt(){
A.write();
}
}
let b = new B();
b.doIt();
Option 2, you instantiate the class in the constructor of the other like so:
class A {
write(){
console.log("Non-static write() called from class A");
}
}
class B {
constructor() {
this.a = new A();
}
doIt(){
this.a.write();
}
}
let b = new B();
b.doIt();
There are a few ways:
I accidentally switched between PHP and Javascript, but the principles are the same for both)
Use static functions:
Normally, you have a this in the class. Say you have this code:
class Car {
let color;
public function setColor(newColor){ this.color = newColor;}
}
let car = new Car();
car->setColor('green')`
The setColor function's this refers to that car. You can make let anotherCar = new Car(), then when you do anotherCar->setColor('red') you only change that car, not the first one. Simplistic: You can create multiple instances.
If you do not need that, but need the class once, you can make it static. A simple way to explain would be "you have a collection of seperate functions, just put into a wrapping class (which doesn't do a lot really)". For instance, you might have some sanatizing methods:
class Sanitize {
static function makeHtmlSave(input){
return doYourMagicHere(input);
}
static function removeXssCode(input){
return doMoreMagicHere(input);
}
}
This way, you can reuse it multiple times. If you want to use it, you do Sanitize::makeHtmlSave(someCode) where you need it. There isn't a Sanitize thing, it's just a wrapper to access the frunctions inside it.
Use extend:
You can extend a class. Say you have a generic class Vehicle, which has some properties (eg a motor, numberWeels, color) and you can extend that with more specific classes:
class Vehicle {
let color;
public function setColor(newColor){ this.color = newColor}
}
class Car extends Vehicle {
let hasAirco = false;
public function hasAirco(newValue){ this.hasAirco = newValue};
}
If you do let car = new Car(), you get a Car object, that extends/enlarges/complements the Vehicle class, so you can use both its (public) functions. Internally, Car can use the functions of Vehicle too.
Just pass it
class One {
// some stuff
}
class Two{
let otherObject;
construct(givenObject){
this.otherObject = givenObject;
}
}
You can now do this let a = new One(); let b = new Two(a);. You can not use the functions of One inside Two, but you can still use a->doSomething(). This solution feels like the easiest, but it almost never is. Classes/objects are tricky stuff, but I've rarely uses this solutions. There are use cases, but often it's a bad smell indicator.
I have an app.js with this code:
var addnote = (title,body) => { /* enter code here */ }
module.exports = {addnote};
Can I add another addnotes function with different parameters to that file?
Function overloading in JavaScript does not exist like in other programming languages such as C# and Java.
What you should be looking to do is pass an object as a parameter that has properties attached and filter them out there..
You could call different functions from your little 'mapping function' just implement the logic there if it isn't big (to keep the code clear).
function foo(parameters){
var title = parameters.title;
var body = parameters.body;
if(parameters.extraProperty){
// oh we have extraProperty passed in too, run a different function?
bar(title, body, parameters.extraProperty); // ??
}
}
foo({title: 'Title', body: 'Body', extraProperty: 'This is extra...'});
If this is your own custom module, you can use the concept of function overriding, where each child class can have its own way to handle something and also have a default way to do things.
class Parent {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hello ${this.name}`);
}
}
class Child1 extends Parent {
constructor(name) {
super(name);
}
greet() {
console.log(`Hey there ${this.name}. This is Child 1`);
}
}
class Child2 extends Parent {
constructor(name) {
super(name);
}
greet() {
console.log(`Hi there ${this.name}. This is Child 2`);
}
}
const o1 = new Child1('Foo')
const o2 = new Child2('Foo')
o1.greet();
o2.greet();
But if you are trying to override a function in an external module(You do not have access to that code, like a library), my suggestion is to create a wrapper and add functionality there.
I have below code to add listeners on EventEmitter,
class MyClass {
init() {
this.listener = new EventEmitter();
this.listener.on('eventName', this.onChange.bind(this));
}
onChange() {
...
}
}
How can I remove the listener from EventEmitter? I can see two different ways:
this.listener.removeListener('eventName', this.onChange)
this.listener.removeListener('eventName', this.onChange.bind(this))
I wonder which one I should use. Whether I need the bind when I remove it?
You can use bind() or you can use an Arrow Function in Node. Arrow Functions will inherit their execution context from the invoking context, which is similar to how bind() works and provides the same functionality.
this.listener.removeListener('eventName', this.onChange)
The above way of removing a listener won't work if it is being called in a removeListener(eventName) style function like the following:
const obj = new MyObject()
obj.init()
// This would blow up with a
// ReferenceError saying you cant call removeListener() on undefined
obj.listener.removeListener('event')
Not sure the use of the init() function when you can use a constructor with the class syntax.
constructor() {
this.listener = new EventEmitter()
this.boundEventNameHandler = () => this.onChange()
this.listener.on('eventName', this.boundEventNameHandler)
}
You can utilize the bound function for removing a listener within the context of a class using this.
let boundEventNameHandler = this.boundEventNameHandler
this.listener.removeListener('eventName', this.boundEventNameHandler)`
An example of this implemented looks like the following
const EventEmitter = require('events')
class MyObject {
constructor () {
this.listener = new EventEmitter()
this.boundEventNameHandler = () => this.onChange()
this.listener.on('eventName', this.boundEventNameHandler)
}
// I would reconsider making this a property of the MyObject class
// instead, make onChange() a local function outside the MyObject
// class definition because onChange in this example is only used by the
// event listener for 'eventName'
onChange () {
console.log('event fired change')
}
removeListeners () {
let boundEventNameHandler = this.boundEventNameHandler
this.listener.removeListener('eventName', boundEventNameHandler)
}
}
For TypeScript / ESNext you can use this:
class MyClass {
constructor() {
this.emitter = new EventEmitter();
this.emitter.on("event1", this.listener1);
this.emitter.on("event2", this.listener2);
this.emitter.removeListener("event1", this.listener1);
this.emitter.removeListener("event2", this.listener2);
this.emitter.emit("event1"); // no output
this.emitter.emit("event2"); // no output
}
public listener1 = (e) => {
console.dir(`listener1`);
};
public listener2 = (e) => {
console.dir(`listener2`);
};
}
Basically you define properties and instantly assign bound functions to them.
This way you use the same function reference in .on and .removeListener.
I'm learning TypeScript and have the following class:
class DetailDriver {
public get driver() {
return super.getEntity();
}
public activate(): breeze.Promise {
var id = this.driver.id(); // this refers to (class) DetailDriver
return promise
.then(getCertificate)
.fail(somethingWrong);
function getCertificate() {
var id = this.driver.id(); // this refers to any
return ...
}
}
}
As you can see on the above code, the first call to this refers to my class DetailDriver. That's good. The second call to this (inside getCertificate) refers to any. That's not what I need. I need to refer to my class DetailDriver.
How to proceed?
Thanks.
Well,
According to section 4.9.2 of the TypeScript Language Specification you should use fat arrow syntax to preserve the scoping for this.
return promise
.then(() => return.this.id;)
.fail(somethingWrong);
Then the this keyword is properly determined to be a Driver.
For reference, you could also just do:
class SomeClass {
public someMethod() {
// Do something
}
public anotherMethod() {
var that = this; // Reference the class instance
function someFunction () {
that.someMethod();
}
}
}
You could refactor to something like this:
class DetailDriver {
public get driver() {
return super.getEntity();
}
public activate(): breeze.Promise {
var id = this.driver.id(); // this refers to (class) DetailDriver
return promise
.then(this.getCertificate.bind(this)) // <- important part
.fail(somethingWrong);
}
// new method function here
private getCertificate() {
var id = this.driver.id(); // this refers to any
return ...
}
}
Using the function keyword anywhere in your class will make any reference to this keyword refer to that function rather than the outer class. Generally, you want to avoid defining functions inside of classes, unless you use the "fat arrow" syntax. That would look like this:
class DetailDriver {
public get driver() {
return super.getEntity();
}
public activate(): breeze.Promise {
var id = this.driver.id(); // this refers to (class) DetailDriver
return promise
.then(() => { // <- important part
var id = this.driver.id(); // this refers to any
return ...
})
.fail(somethingWrong);
}
}
I have been stuck for 4 days so it is going on SO!
Please reference the PreloadJS Definitions:
https://github.com/borisyankov/DefinitelyTyped/blob/master/preloadjs/preloadjs.d.ts
From line 26.
My question is, how do you addEventListener in typescript while also binding it to the class scope?
imagine (semi psudo):
class Test
{
private _loader:createJS.AssetLoader;
constructor()
{
this._loader = new createJS.AssetLoader();
this._loader.addEventListener(???????????);
this._loader.load();
}
onLoadComplete(???): ???
{
}
}
Using TypeScript 0.9.1.0:
class Test
{
private _loader: createJS.AssetLoader;
constructor()
{
this._loader = new createJS.AssetLoader();
this._loader.addEventListenerthis.onLoadComplete);
this._loader.load();
}
onLoadComplete = () =>
{
console.log(this.whatever);
};
}
The important part here is using the member initialization syntax (member = value) for onLoadComplete and using a fat arrow lambda expression (() => { ... }) for the function. This will correctly capture the this context in the callback.