Constructor and class properties within javascript mixins - javascript

I am trying understand mixins in javascript and all the examples and articles i have read so far talk about adding methods and not properties.
I have found Alex Jover Morales' article really useful and i have slightly modified his example to include an additional mixin and constructors with new properties within the mixins here.
Is what i have done below an anti-pattern?
Is there a problem with having a constructor and properties within a mixin?
Is there a problem with calling super() within each mixin's contructor?
const PlayMixin = superclass => class extends superclass {
constructor(args) {
let { favouriteGame } = args
super(args);
this.favouriteGame=favouriteGame;
}
play() {
console.log(`${this.name} is playing ${this.favouriteGame}`);
}
};
const FoodMixin = superclass => class extends superclass {
constructor(args) {
let { genericFood } = args
super(args);
this.genericFood=genericFood;
}
eat() {
console.log(`${this.name} is eating ${this.genericFood}`);
}
poop() {
console.log("Going to 💩");
}
};
class Animal {
constructor(args) {
let {name} = args
this.name = name
}
}
class Dog extends PlayMixin(FoodMixin(Animal)) {
constructor(...args) {
super(...args)
}
bark() {
console.log("Woff woff!")
}
haveLunch() {
this.eat();
this.poop();
}
}
const jack = new Dog({name:"Jack", genericFood:"lobster",
favouriteGame:"chess"});
jack.haveLunch();
jack.play();
.as-console-wrapper { max-height: 100%!important; top: 0; }

Is what i have done below an anti-pattern?
No, it is not.
Is there a problem with having a constructor and properties within a mixin?
No, as long as you call super(...) in a manner that it works for all mixed in classes.
Is there a problem with calling super() within each mixin's contructor?
No, super always points to the extended class, there isno problem in calling that constructor.

Related

How to implement abstract factory in js?

I want to implement very simple abstract factory pattern but I am facing this error.
Also am I doing something wrong with my code? How can I improve this to make it work properly?
Must call super constructor in derived class before accessing 'this' or returning from derived constructor"
You need to call super() inside constructor:
class MakeMeatPizza extends MakePizza {
constructor() {
super();
super.createPizza("meat");
}
}
So thanks to this beautiful answer:
The rules for ES2015 (ES6) classes basically come down to:
In a child class constructor, this cannot be used until super is called.
ES6 class constructors MUST call super if they are subclasses, or they must explicitly return some object to take the place of the one
that was not initialized.
And the code looks like this:
class Pizza {
constructor(name,size,type) {
this.name = name;
this.size = size;
this.type = type;
}
prepare() {
console.log("prepare");
}
bake() {
console.log("bake");
}
deliver() {
console.log("deliver");
}
}
class MeatPizza extends Pizza {
constructor() {
super("Chicago style pizza", "Large", "Meat")
}
}
class VegePizza extends Pizza {
constructor() {
super("Only natural", "Large", "Vegetarian")
}
}
class MakePizza {
createPizza(type) {
switch (type) {
case "meat":
return new MeatPizza()
case "vege":
return new VegePizza()
default:
throw new Error("Something went wrong...");
}
}
}
class MakeMeatPizza extends MakePizza {
constructor() {
super()
}
create() {
return this.createPizza("meat")
}
}
class MakeVegePizza extends MakePizza {
constructor() {
super()
}
create() {
return this.createPizza("vege")
}
}
class OrderPizza {
}
const test = new MakeVegePizza().create();
console.log(test)

Child class member initializer overriding parent class constructor

If I have a super class that is supposed to be able to set some initial properties on a new instance, why do the members of the child class seem to override the constructor of the super class?
class Base {
constructor(fields) {
Object.assign(this, fields)
}
}
class Foo extends Base {
time = 0;
// calling super seems to make no difference
// constructor(...args) {
// super(...args)
// }
}
const foo = new Foo({ time: 22 })
console.log(foo.time) // is 0, should be 22?
If I remove time = 0; from the child class it works as expected but my linter breaks.
How can I retain the member initializer, i.e., time = 0; in the child class but have the super class be able to set properties in its constructor?
Instances may only be accessed after a super call is complete. Without the class field, look at the error:
class Base {
constructor(fields) {
Object.assign(this, fields)
}
}
class Foo extends Base {
constructor(...args) {
this.time = 0;
super(...args)
}
}
const foo = new Foo({ time: 22 })
Must call super constructor in derived class before accessing 'this' or returning from derived constructor
Class fields follow this same logic by assigning to the instance only after the super call is complete. So
class Foo extends Base {
time = 0;
constructor(...args) {
super(...args)
is equivalent to
class Foo extends Base {
constructor(...args) {
super(...args)
this.time = 0;
In other words - the properties assigned inside the constructor (or class field) of a subclass is designed to override the properties assigned in a superclass constructor.
but have the super class be able to set properties in its constructor?
One approach would be to the default value for the time property to 0, then pass and use that instead of a class field:
class Base {
constructor(fields) {
Object.assign(this, fields)
}
}
class Foo extends Base {
constructor({ time = 0, ...rest }) {
super({ time, ...rest })
}
}
const foo = new Foo({ time: 22 })
console.log(foo.time)

Accessing base class properties in derived class using prototype/Access base class properties in the decorator

I am using typescript and i am writing a custom decorator for one of my angular class. I want to access the base class method in the child class decorator. Or access base class methods using the child class prototype. Is there any way to do this? Problem explained in detail below.
Scenario
I have a base class which is like
export class Base {
public init() {
console.log('My base class function');
}
}
And i have a derived class which extends this base class
export class Child extends Base {
}
What i am trying to do
I am trying to write a decorator for the derived class something like
#TestDecorator(['init'])
export class Child extends Base {
}
which will call the init method from the base class.
What is the issue
To do the above scenario, i have written code something like below
export function Tool<T extends Base>(methods: any[]) {
return function (target: Function) {
methods.forEach((item) => {
if (item === 'init') {
target.super.init() // Stuck here
}
})
}
}
I am not understanding how to make the following line work
target.super.init() // Stuck here
Please help me with the solution. I am stuck. Thanks
I believe you are looking for something like this:
export function Tool<T extends Base>(methods: any[]) {
return function (target: Function) {
return class extends target {
constructor(...args: any[]) {
super(...args)
methods.forEach((item) => {
if (item === 'init') {
super.init( );
}
})
}
}
}
}
To expand on Paulpro's answer, since the decorator function is returning a substitute for the constructor of the class that it is decorating, it must maintain the original prototype.
In the following example, there is an error due to the missing init() method in TestDecorator<Base>.
Typescript Playground Demo
class Base {
public init() {
console.log('My base class function');
}
}
function TestDecorator<T extends Base>(methods: any[]) {
return function (target: any) {
return class extends target {
constructor(...args: any[]) {
super(...args)
methods.forEach((item) => {
if (item === 'init') {
super.init( );
}
})
}
}
}
}
#TestDecorator(['init']) // Error: Property 'init' is missing in type 'TestDecorator<Base>.(Anonymous class)' but required in type 'Child'.
class Child extends Base {
}
let c = new Child();
Corrected Decorator
function TestDecorator<T extends Base>(methods: any[]) {
return function (target: any) {
return class extends target {
init() {} // Define init()
constructor(...args: any[]) {
super(...args)
methods.forEach((item) => {
if (item === 'init') {
super.init( );
}
})
}
}
}
}
Class Decorators
If the class decorator returns a value, it will replace the class declaration with the provided constructor function.
NOTE: Should you choose to return a new constructor function, you must take care to maintain the original prototype. The logic that applies decorators at runtime will not do this for you.

Using an array to apply multiple mixins

Using ES6 is there a way to apply multiple mixins which are defined in an array? The mixins would be defined as such:
const mixins = Array('Mixin', 'Mixin2');
Then creating a mixin with:
export const Mixin = function (superClass) {
return class extends superClass {}
And using the mixin with:
export class MyClass extends MultipleMixins(BaseClass)
You can use reduce() over the array of mixins, pass in a base class and keep returning a new mixed class. This will just apply all the mixing in order:
class BaseClass {
constructor(name) {
this.name = name
}
}
// adds an uppercase
const Mixin = function(superClass) {
return class extends superClass {
get uppercase() {
this.name = this.name.toUpperCase()
return this
}
}
}
//adds a reverse
const Mixin2 = function(superClass) {
return class extends superClass {
get reverse() {
this.name = [...this.name].reverse().join('')
return this
}
}
}
let mixins = [Mixin, Mixin2]
let MixedClass = mixins.reduce((base, mix) => mix(base), BaseClass)
let instance = new MixedClass('mark')
// use the new methods
console.log("reversed & uppercase:", instance.uppercase.reverse.name)

OOP for multi-component classes

I'm having a very hard time writing modules that are comprised of typically 3-5 smaller modules or classes. The issue arises when these sub components need to be extended, but the main module is already creating and implementing their base versions.
Example
// main module design
class Car {
constructor() {
this.horn = new Horn();
this.horn.on('honk', function() {
console.log('honked');
})
}
}
class Horn {
constructor() {
this.sound = 'hornA.wav'
}
}
// extended module design
class RaceCar extends Car {
constructor() {
super();
this.horn = new RaceHorn();
// event handler is now missing
}
}
class RaceHorn extends Horn {
constructor() {
super();
this.horn = 'hornB.wav'
}
}
This is indeed a very simplified example, while my true issues involve modules with more components and more setup requirements. I do understand that I can put things into another init or setup or similar function, but to me it seems like I'm doing something wrong inherently.
I heavily suggest passing the values through parameters, for what concerns this issue. Default parameters can help a lot
// main module design
class Car {
constructor( parametrizedHorn=Horn ) {
this.horn = new parametrizedHorn();
this.horn.on('honk', function() {
console.log('honked');
})
}
}
class Horn {
constructor() {
this.sound = 'hornA.wav'
}
}
// extended module design
class RaceCar extends Car {
constructor() {
super( RaceHorn );
}
}
class RaceHorn extends Horn {
constructor() {
super();
this.sound = 'hornB.wav'
}
}
This can get very messy, so instead you can use initialization methods
// main module design
class Car {
constructor() { ... }
init( hornType=Horn ){
this.initHorn( hornType );
}
initHorn( hornType=Horn ){
this.horn = new Horn();
this.horn.on('honk', function() {
console.log('honked');
})
}
}
class Horn {
constructor() {
this.sound = 'hornA.wav'
}
}
// extended module design
class RaceCar extends Car {
constructor() {
super();
}
init( hornType=RaceHorn ){
this.initHorn( hornType );
}
}
class RaceHorn extends Horn {
constructor() {
super();
this.sound = 'hornB.wav'
}
}
const raceCar = new RaceCar();
raceCar.init();
These are two modular patterns that you can use, and depending on the kind of structure you're already using, you can judge which one is best. The trick in the end is to parametrize the contents of your class, or modularize it even further with methods

Categories