Composition and Mixins with JavaScript - javascript

I am learning about compositions in Javascript. So I want to ask if this is correct way of doing things.
I made some exercises that look like this:
class Animal {
// constructor() {
// }
eat = () => {
console.log("this creature is eating");
}
}
const AnimalsWithWings = superclass => class extends superclass {
constructor(Feathers, ...args) {
super(...args);
Object.assign(this, { Feathers });
}
}
const CanDive = superclass => class extends superclass {
// constructor( ...args) {
// super(...args);
// }
dive = () => {
console.log("Can Dive");
}
}
class Duck extends AnimalsWithWings(CanDive(Animal)) {
constructor(eats, ...args) {
super(...args);
Object.assign(this, { eats });
}
}
const duffy = new Duck("watermelon", true);
console.log(duffy);
duffy.dive();
duffy.eat()
I am still in learning process so I just need some pointers.

Did it do more or less what you expected? Then, sure, it's a correct way to do it, whatever "correct" means here.
It looks to me like it does what it was meant to do when I pop it into the console. I can't really say much more on your code specifically because I'm not sure what concrete domain it's trying to model, aside from maybe breaking down Ducks into atomic pieces.
If you're going to do it this way, though, I'd personally prefer to use a params object instead of just changing the constructor signature like that with AnimalsWithWings. That way, the order of extra parametrizations doesn't depend on the order in which the mixins were applied, which I would consider a Surprise. Surprises are bad.
const AnimalsWithWings = superclass => class extends superclass {
// Everyone receives the same `params` object.
// They only take what they know about, and ignore the rest.
constructor(params) {
super(params);
Object.assign(this, { Feathers: params.Feathers });
}
}
Even more personal opiniony, I'd name them WithDiving and WithWings instead, just to keep a somewhat consistent naming scheme, and to better imply that these are modifiers, not "real" base classes.
Your code does saddle every Duck with a prototype chain 4 prototypes long, but eh, whatever. If it somehow becomes a performance problem then you can create a utility function to optimize the mixin process or something. Flatten the prototypes, maybe.
Your code does also let you call super.method() in methods, though it's debatable whether you should ever use that in a mixin at all. I'd say you shouldn't, unless you want your mixins to implicitly depend on each other, which is a Surprise.
There are plenty of other ways of doing mixins, too.
You could create a utility function to flatten all the prototypes into a single new one and return a base class from that which you extend. (Just be sure to iterate property descriptors rather than just using Object.assign() when doing that flattening, if you want to properly handle things like get/set accessors, etc.)
You could eschew Classes and just directly create prototype objects and use Object.create() to create instances. (same thing about iterating property descriptors.)
You could create a Duck prototype using a bunch of iterative calls to Object.create() instead of iteratively extending base classes.
You could control the additional behaviors with helper Controller Classes instead of composing behavior directly into the base.
You could deal just in plain objects with data, and pass the objects to functions that expect the object to have certain properties on it in order to do things. (Amusingly, called "duck typing") I'll grant that's not really mixins, just calling functions, but if it accomplishes the same thing in effect...
Probably a bunch others I can't really think about at the moment. It's all sticking sets of behaviors onto some base thing.

Related

abstract static method in TypeScript

I'm looking for a way to implement an abstract static method in TypeScript.
Here an example:
abstract class A {
abstract static b (): any // error
}
class B extends A {
static b () {}
}
This is allowed with public, private and protected methods but not with a static method. TypeScript returns the following error:
'static' modifier cannot be used with 'abstract' modifier.ts(1243)
Is there any workaround?
I think the reasoning is generally as follows:
Abstract methods are placeholders that a concrete subclass is supposed to implement.
The abstract base class will have a concrete method which uses this abstract placeholder, and thus requires it to be implemented; without this, abstract methods make little sense at all. E.g.:
abstract class Foo {
bar() {
return this.baz() + 1;
}
abstract baz(): int;
}
This can only be called on a concrete instance, e.g.:
function (foo: Foo) {
let val = foo.bar();
...
}
The result will be different here depending on what concrete subclass of Foo you get, but it's all guaranteed to work.
Now, what would this look like with static methods?
abstract class Foo {
static bar() {
return this.baz() + 1;
}
abstract static baz(): int;
}
function (F: typeof Foo) {
let val = F.bar();
...
}
This looks somewhat logical and like it should work, shouldn't it? But:
If you write this, there's basically no difference to passing instances of classes, except for the awkward typeof typing. So, why?
If you never instantiate the class, what's the point of a class?
If your static method does instantiate an object, if it acts as an alternative constructor—which is entirely legitimate—then calling F.bar() to get a new instance, you'd expect to get an instance of F, but you might be getting an instance of some subclass of it. And that's arguably not desired.
To rephrase the chain of argument here:
If you're going to use static methods at all, they should act as alternative constructors and return an instance, otherwise they have little business being part of the class.
Alternative constructors should return an instance of the class that you called them on. Calling F.bar() and not getting an instance of exactly F is… weird?
If you're going to call static alternative constructors on classes, you're going to want to call them on specific classes and not variables as shown above, because otherwise you really won't know what you're getting (see point above).
Therefore, there's no real use case for an abstract static method, either as direct alternative constructor nor as helper function for one, since that would lead to a violation of one of the above points one way or another.
As far as I see, this is pretty much the classical thinking that lead to abstract static methods not being a thing. Certain technical limitations in Java may have contributed to that and/or lead to said technical limitations in the first place.
In Javascript one can argue that this makes less sense, since even classes are objects and it would be entirely feasible to use them as described above, and in certain situations it may even make sense. But here we are.

Call Class Constructor as Function on Instance

I am writing an object pool class that holds onto old unused objects, and when a new one is needed can use one of its reserves (Without the expensive creation of objects). However, I want to be able to save any data to this pool (not limited to something such as extends Poolable). The problem is that most object pools require something like this:
class MyData {
constructor(str) {
this.reset(str)
}
reset(str) {
this.str = str;
}
}
So that when a new instance is needed, the pool can call oldInstance.reset(prams). As stated before, I do not want this (Working with lots of third party tools that I am not feeling like writing wrappers for), so my data looks like this:
class MyData {
constructor(str) {
this.str = str;
}
}
When the pool needs to reset an instance, I need to be able to call the constructor as a function and set the this value to the instance that is being wiped.
This is super easy with old classes that used function/prototype syntax when they were made:
const MyClass = function(str) {
this.str = str;
}
// Reset an instance
const instance = new MyClass("foo");
MyClass.apply(instance, ["bar"])
// done
However when I do that for classes, it complains that you can not use a class without the new keyword. How do I go about doing this?
Fiddle: https://jsfiddle.net/wuqek405/
Edit:
As I said, most object pools need a reset function. I am trying to use the constructor as this function, because it is supposed to “set up” the instance. Another solution would be to generate this reset function based on the class. However, I want it to be as fast as possible, so terribly hacky solutions such as stringifying the class and evaling the constructor wouldn’t be optimal.
This is not possible. A class is not callable1, only constructable - which means creating a new object. This was new in ES6, which overhauled the inheritance model of classes and introduced super. Also, ES2022 will introduce class fields, which also get created during construction without being mentioned in the constructor code.
Your only option is to use only ES5 function-based classes, a transpiler, or writing explicit reset methods.
1: technically, it is callable (typeof C == 'function'), but [[Call]] will always throw an exception

Accessing an Objects instance constructor for cloning

TLDR: Are there any pitfalls in using this.constructor(props) in a class method to duplicate that class?
I have a use case where defining class models for entities proves quite useful for management and processing throughout a complex system.
These entity model classes all require a set of common methods.
I do not wish to duplicate code across multiple entity model classes. I am exploring the use of a BaseEntityModel class which can be extended by all other model classes requiring the common functionality.
One of these methods is a clone() method, which provides a deep clone of the object as the ExtendedEntityModel type.
clone() will be declared on the BaseEntityModel class. I want to preserve the class name and properties when calling clone from a ExtendedEntityModel object which has extended BaseEntityModel class.
I need a way of creating a new instance of a ExtendedEntityModel through the BaseEntityModel without having to explicitly know what it is when calling clone(). In otherwords, I wanted to avoid using new ParentClassEntity() because the BaseEntityModel methods need to work for all extensions of it.
I did some searching but could not find much on this pattern. After some tinkering, I found that calling this.constructor(props) does exactly what I want. See below class, and clone() function
BaseEntityModel.js
class BaseEntityModel {
constructor({ entityType, dataKeys }) {
this.entityType = entityType;
this.dataKeys = dataKeys;
}
/**
* Provide deep clone of self, Preserving extended class properties
*/
clone() {
return new this.constructor(JSON.parse(JSON.stringify(this))); //<-- here, this.constructor
};
}
ExtendedModel.js
class ExtendedModel extends BaseEntityModel {
constructor({param1, param2} : ExtendedModel) {
super({ entityType, dataKeys });
this.param1 = param1;
this.param2 = param2;
}
}
and then usage
var extendedModel1 = new ExtendedModel({param1, param2};
var clonedExtendedModel = extendedModel1.clone(); //returns a cloned version and of type ExtendedModel
I cannot find much on calling this.constructor. Can anyone provide some insight into why this might be a bad choice, or confirm that this is not a javascript bomb waiting to go off?
See CodeSandbox of working implementation

Augment all objects with another class properties/methods

Imagine I have two classes
class Point {
}
class EditablePoint extends Point {
// list of additional properties and methods
}
function edit(editablePoint) {
//
}
let point = new Point();
edit(point);
How can I augment all objects with another class methods/properties?
Any prototype hacks don't work for me. It seems I can't get around this instead of recreating the objects or assigning manually additional properties of the class or using a loop. I would like some one line clean solution.
Disclaimer: this looks like an antipattern with which I agree however I have huge arrays of Points and only in super rare occassions I need to edit them and I want to do so in-place without introducing any copying as arrays are HUGE.
Why I don't like having all points have editable properties and want to have them on the prototype:
each object instance becomes larger and that's the least I want
it doesnt communicate well to the code reader
What you're asking is technically feasible:
let pointProto = Point.prototype;
let editProto = EditablePoint.prototype;
for (let memberName in editProto) {
if (typeof editProto[memberName] === 'function'
&& !(memberName in pointProto)) {
pointProto[memberName] = editProto[memberName];
}
}
This will achieve what you're describing: all instances of the Point class will now have the same methods as EditablePoint instances do (the !(memberName in pointProto) clause ensures we don't overwrite methods that were already defined).
However, as you yourself have said, this is a massive antipattern, since at this point you're blurring the line between the two classes to a point where it's unclear why they even exist as separate classes; seeing an array of Points being used as if they were EditablePoints is a 100% sure way to confuse anyone who's reading your code. If you think you need something like this, it's probably better to rethink your model structure.
You could use Object.assign to add properties and setPrototypeOf to add methods by setting prototype of EditablePoint to prototype of Point, but since EditablePoint inherits from Point you will still have Point methods.
class Point {
constructor() {
this.point = true;
}
foo() {
console.log('foo')
}
}
class EditablePoint extends Point {
constructor() {
super();
this.editable = true;
}
bar() {
console.log('bar')
}
}
function edit(editablePoint) {
Object.assign(editablePoint, new EditablePoint);
Object.setPrototypeOf(editablePoint, EditablePoint.prototype)
}
let point = new Point();
edit(point);
console.log(point)
point.foo();
point.bar();
I think you just should not make an EditablePoint class that inherits from Point and has all the methods necessary for editing. Instead, use composition over inheritance, and make a class
class PointEditor {
constructor(point) {
this.point = point;
…
}
…
}
with all the methods necessary for editing the point associated to the editor. This way, you can instantiate as few / as many PointEditors for the points in your array that you want to edit, without changing anything about how the points in the array are created.

How to avoid ES6 Javascript class inheritance naming collisions

How does one avoid naming collisions from class inheritance in ES6 Javascript?
Large ES6 Javascript applications use a lot of inheritance, so much so that using generic names in a base class can mean debugging headaches later when creating derived classes. This can be a product of poor class design but seems more to be a problem with Javascript being able to scale up smoothly. Other languages provide mechanisms to hide inherited variables (Java) or properties (C#). Another way to alleviate this problem is to use private variables which isn't something Javascript has.
Here's an example of such a collision. A TreeObject class extends an Evented object (to inherit evented functionality) yet they both use parent to store their parent.
class Evented {
constructor(parent) {
this.parent = parent;
}
}
class TreeObject extends Evented{
constructor(eventParent, treeParent) {
super(eventParent);
this.parent = treeParent;
}
}
While this example is a bit artificial, I've had similar collisions in large libraries like Ember where terminology between the library and the end application overlap quite a bit causing me hours wasted here and there.
This really seems to be a design issue (use smaller objects and flatter hierarchies), but there's a solution to your problem as well: symbols!
const parentKey = Symbol("parent");
export class Evented {
constructor(parent) {
this[parentKey] = parent;
}
}
import {Evented} from "…"
const parentKey = Symbol("parent");
class TreeObject extends Evented {
constructor(eventParent, treeParent) {
super(eventParent);
this[parentKey] = treeParent;
}
}
They will reliably prevent any collisions, as all symbols are unique, regardless of their descriptors.
Large ES6 JavaScript applications use a lot of inheritance.
Actually, most of them don't. In a framework like Angular, one level of inheritance from the platform-defined classes is most common. Deep inheritance structures are brittle and best avoided. A large app might have some cases of two user levels of classes, A > B, where A contains a bunch of common logic and B1 and B2 are light specializations, such as a different template for a component, at a level that does not give rise to concerns about collisions.
Your example of Evented is semantically not really a parent class; it's more in the nature of a mixin. Since JS doesn't really handle mixins well, instead of deriving Tree from Evented, I'd hold the evented object as a property:
class TreeObject {
constructor(eventParent, treeParent) {
this.evented = new Evented(eventParent);
this.parent = treeParent;
}
send(msg) { this.evented.send(msg); }
}
If you really want to design Evented for use as a mixin-like superclass, then it's the responsibility of the designer to make member variables as unique as possible, as in
export class Evented {
constructor(parent) {
this.eventedParent = parent;
}
}
or use a symbol, as proposed in another answer. Or, consider using a map:
const parents = new Map();
class Evented {
constructor(parent) {
parents.set(this, parent);
}
sendParent(msg) {
parents.get(this).send(msg);
}
}
I've had similar collisions in large libraries like Ember
I haven't. Ember classes typically define few members, and the methods they define are well-defined and well-known.
Of course, the real solution is to use a typing system such as TypeScript. It does provide private members/methods. If a method does need to be public, TS will not let you define a method by the same name on the subclass unless the signatures match.
One possible way of minimizing this, that I can think of right the top of my head, is wrapping the internal state of your object, for your example that would be:
class Evented {
constructor(parent) {
this.eventedState = {
parent : parent
}
}
}
And apply the same pattern to all the classes. Obviously this still means that you have one property per object that might collide, but it reduces the chance of collision. The downside of this is that its not perfect and refactoring your code to use this pattern might be quite painful.

Categories

Resources