When to use Javascript decorators vs inheritance - javascript

I am learning how to use decorators in Javascript and am confused as to when its appropriate to use class inheritance vs decorators?
For instance the following uses decorators to add a method to a class. It also overwrites one of the decorated classes methods.
import $ from "jquery";
function write(msg) {
$("#app").prepend(msg);
}
function clean(constructor) {
return class extends constructor {
foo() {
write("<h1>from the Decorated class</h1>");
}
bar() {
write("I haz bar");
}
};
}
#clean
class Hello {
foo() {
write("<h1>from the undecorated class</h1>");
}
}
const h = new Hello();
h.();
AFAIK, with inheritance — if the parent and child classes have the same methods, it would be flipped: child would overwrite the parent. In a situation where I would NOT want this to happen, decorators would be the desired choice. Is that correct?
P.S.
I know decorators not officially in the language yet.

Related

What is the correct way to pass a large number of attributes to a parent constructor if it's possible for an attribute to need a reference to `this`?

I have a general paradigm from which I'd like to create objects. Currently, I have it written in such a way that the parent class builds the paradigm from a configuration object given to it by the subclass. The configuration object defines all of the class' characteristics, and the class is also able to overload any methods that need to be slightly different from one implementation of the paradigm to the next.
This approach is convenient as it allows me to:
avoid a long list of parameters for the constructor,
prevent the subclass from needing to define the attributes in any particular order, and
prevent the subclass from needing to explicitly create instances that the parent constructor can imply from the configuration.
As much as I liked the cleanliness and flexibility of sending this configuration object to super(), I've found a glaring limitation to this approach:
I cannot make a reference to this until super has completed.
class Paradigm {
constructor(config) {
this.name = config.name;
this.relationships = config.relationships;
}
}
class Instance extends Paradigm {
constructor(name) {
super({
name: name,
relationships: {
children: {
foo: new Foo(this) // Error: `this` referenced before `super`
}
}
});
}
}
class Foo {
constructor(parent) {
this.parent = parent;
}
}
NOTE: The configuration object is typically significantly larger than this, which is why I'm using it at all.
I can imagine there are many ways to approach this, but I'm most interested in what would be the correct way.
Should I be using functions in place of statements that require a reference to this and only execute them when they are needed?
Should I keep the same approach, but simply replace super({...}) with this.build({...})?
Should I abandon the idea of the configuration object altogether and instead take a different approach?
The fact that this is needed before super often suggests that a constructor contains logic that should be moved to separate method.
In some cases some workarounds are possible, as long as they don't cause problems. E.g., a part of configuration that requires this can be instantiated later:
constructor(name) {
const config = {
name: name,
relationships: {
children: {};
}
};
super(config);
config.relationships.children.foo = new Foo(this);
}
Of course, this presumes that relationships is just stored as a reference and isn't processed on construction.
In more severe cases class hierarchy (Instance and Paradigm) may be required to be converted to ES5 classes, since ES6 classes can extend regular functions but not vice versa.
A possible way would be to change your Paradigma to this:
class Paradigm {
constructor(config) {
Object.assign(this, config(this));
}
}
So that you can do:
class Instance extends Paradigm {
constructor(name) {
super((context) => ({
name: name,
relationships: {
children: {
foo: new Foo(context)
}
}
}));
}
}
But actually this is a bad way of solving it...
Another way could be to do it the other way round:
const Paradigma = cls => class Paradigma extends cls {
constructor(...args){
super(config => setTimeout(() => Object.assign(this, config), 1), ...args);
}
};
So you can do:
const Instance = Paradigma(class {
constructor(build, name){
build({name});
}
});
new Instance("Test");

Arrow vs classic method in ES6 class

Is there any reason to write classic syntax of ES6 methods?
class MyClass {
myMethod() {
this.myVariable++;
}
}
When I use myMethod() as callback on some event, I must write something like this (in JSX):
// Anonymous function.
onClick={() => { this.myMethod(); }}
// Or bind this.
onClick={this.myMethod.bind(this)}
But if I declare method as arrow function:
class MyClass {
myMethod = () => {
this.myVariable++;
}
}
than I can write just (in JSX):
onClick={this.myMethod}
The feature you are using is not part of ES6. It's the class fields proposal. It allows you to initialize instance properties without having to write a constructor. I.e. your code:
class MyClass {
myMethod = () => {
this.myVariable++;
}
}
is exactly the same as
class MyClass {
constructor() {
this.myMethod = () => {
this.myVariable++;
};
}
}
And this also shows you what the difference is between a normal class method an a method created via a class field:
A normal method is shared between all instances of the class (it is defined on the prototype)
A "class field method" is created per instance
So all the same as reasons as presented in Use of 'prototype' vs. 'this' in JavaScript? apply, but in short:
Use "class field methods" if you need a method per instance. Such is the case for event handlers that need to access the current instance. Access to this also only works if you are using an arrow function.
Use normal class methods in all other cases.

Wrapping a constructor with javascript method decorator

Referenced:
Override constructor with an class decorator?
http://blog.wolksoftware.com/decorators-metadata-reflection-in-typescript-from-novice-to-expert-part-ii
I'd like to wrap a classes constructor with a custom one when applying a method decorator (ie in the same way you could do with a class decorator). I require this as I have another component that will call into the class, and will execute the method that was decorated. Its not hard to have the component execute the method that was decorated, however because the decorator is run before the class is instantiated the decorated method will not be associated with a class instance and therefore will not have access to any of the classes state (ie this == undefined). So I want to be able to provide the method reference to the component during class instantiation (ie constructor) so that it is bound to the current instance.
So I'd like to do this (typescript):
class Foo {
constructor(private value) { }
#execute
bar() {
return this.value;
}
}
Which would have the same affect as doing this:
class Foo {
constructor(private value) {
StaticComponent.AddReference(this.bar().bind(this));
}
bar() {
return this.value;
}
}
If my other component has a reference to bar() it should be able to execute it with bar having full access to its instance.
I've tried to override the prototype of the target etc in a similar way you might do so for a class decorator, but couldn't get that to work.

Applying Behaviors with JS Mixins in Polymer 2

I want a custom element I'm defining to have the Polymer.IronScrollTargetBehavior in Polymer 2.
In Polymer 1, this can be done by adding it to the behaviors array:
Polymer({
is: 'my-element',
behaviors: [Polymer.IronScrollTargetBehavior]
});
In the Polymer 2 upgrade guide, it says that you should:
Implement "behaviors" as mixins that return class expressions.
In the linked article, it explains how you can use the following syntax for mixins:
let MyMixin = (superclass) => class extends superclass {
foo() {
console.log('foo from MyMixin');
}
};
class MyClass extends MyMixin(MyBaseClass) {
/* ... */
}
I mostly get what's going on here (although I find the mixin syntax difficult to wrap my mind around), and I can get sample code to work.
What I haven't been able to do is apply this concept to Polymer.IronScrollTargetBehavior, and create a mixin for it. Since that behavior is already defined as an object, I don't know where to fit it in.
So, how do I implement the proper mixin in this scenario, or if I'm on the wrong path, how to I apply one of the defined Polymer behaviors to my custom element in Polymer 2?
You can use the Polymer 2 hybrid behaviours as mixins by extending
Polymer.mixinBehaviors(behaviors, klass) where
- behaviors is the Behavior object or array of behaviors
- klass is the Element class.
i.e.
<dom-module id="element-name">
<template><!-- ... --></template>
<script>
class MyElement extends Polymer.mixinBehaviors([MyBehavior, MyBehavior2], Polymer.Element) {
static get is() { return 'element-name' }
/* ... */
}
customElements.define('element-name', MyElement);
</script>
</dom-module>
For more detailed information search the Polymer source code for mixinBehaviors method: polymer/lib/legacy/class.html
worth reading: https://www.polymer-project.org/2.0/docs/upgrade#mixins
Polymer 2.0 has a compatibility layer that still supports the old Polymer function syntax. Most of the 2.0 preview elements, if not all, still retain the old syntax. The breaking changes are mostly in the dom-module markup.
If you are composing new elements, it is recommended you switch over to the class based syntax. If however you are porting 1.0 elements to 2.0 and those elements rely on Polymer behaviors, I don't think you much choice at this juncture but to retain the old syntax.
In the class-based syntax you can fluently simulate Element multiple inheritance of class mixins with something like this
let Mixin = (superclass) => new MixinBuilder(superclass);
class MixinBuilder {
constructor(superclass) {
this.superclass = superclass;
}
with(...mixins) {
return mixins.reduce((c, mixin) => mixin(c), this.superclass);
}
}
const MyMixin = subclass => class extends subclass {
_test(){
}
}
const MyMixinTwo = subclass => class extends subclass {
_testTwo(){
}
}
class MyElement extends Mixin(Polymer.Element).with(MyMixin,MyMixin2) {
static get is() { return 'my-element' }
}
You can separate the MixinBuilder into its own file and then reference it as an Html Import dependency whenever composing elements that use mixins.

Subclassing a decorated JavaScript Class

I'm decorating a class to provide arguments to it's constructor, the problem arises when I try to subclass this class:
#decorate('foo', 'bar')
class Foo {
constructor(foo, bar) {}
}
class Bar extends Foo {}
function decorate(foo, bar) {
return function(ctor) {
return ctor.bind(null, foo, bar);
};
}
The above won't work because of the null context passed to the constructor (at least I think that is the root of the issue). When used with Babel, this is the following error: " Object prototype may only be an Object or null: undefined"
Is there any way to both decorate the parent class and extend the child?
Trying to extend a function that has been created via .bind is not going to work for you. Bound functions have tons of special logic around them. Bound functions do not have a .prototype property, which means extending a bound function would mean there was no prototype chain to follow. The proper way to implement this decorator would be to do
function decorate(foo, bar) {
return ctor => class extends ctor {
constructor(...args){
super(foo, bar, ...args);
}
};
}

Categories