- This answer is a community Wiki. Please add further ways of dealing with the problem as you find them.
Solutions without salt grains
None as of now
Accept, that Javascript does not support classes in the common way.
Solutions that come with a grain of salt
Use arrow-functions on callback
runCallback((...params) => myClass.foo(...params))
Use bind
on methods when used as callback
runCallback(myClass.foo.bind(myClass))
Why does it work?
this
for foo
gets specifically set to myClass
Problems:
- Fragility: Might be easy to forget
bind
ing.
- Blackbox principle break: The correctness of method
foo
will depend on how it is called, not how it is defined (however, note that this was the case to begin with)
- Readability: Code gets longer
Use bind
on methods during creation
class MyClass {
bar = 'my bar';
constructor(){
this.foo = this.foo.bind(this);
}
foo() {
console.log(this.bar);
}
}
Reference: https://stackoverflow.com/questions/56503531/what-is-a-good-way-to-automatically-bind-js-class-methods
Problems:
- Maintenance: You need bind methods inside the constructor
- Fragility: It's easy to forget binding on (new) methods
- Memory: Functions will turn to per-instance functions (i.e. each object has their own function object)
use auto-bind
Available as library, or implement it yourself (see below)
class MyClass {
bar = 'my bar';
constructor() {
autoBind(this);
}
foo() {
return String(this.bar);
}
}
Problems:
- Memory: Functions will turn to per-instance functions (i.e. each object has their own function object)
- Fragility: Don't forget to call
autoBind
Implemenation of autoBind
:
/**
* Gets all non-builtin properties up the prototype chain.
**/
const _getAllProperties = (object) => {
const properties: any = [];
do {
for (const key of Reflect.ownKeys(object)) {
properties.push({ obj: object, key: key });
}
} while ((object = Reflect.getPrototypeOf(object)) && object !== Object.prototype);
return properties;
};
function autoBind(self) {
const props = _getAllProperties(self.constructor.prototype);
props.forEach((prop) => {
if (prop.key === 'constructor') {
return;
}
const descriptor = Reflect.getOwnPropertyDescriptor(prop.obj, prop.key);
if (descriptor && typeof descriptor.value === 'function') {
self[prop.key] = self[prop.key].bind(self);
}
});
return self;
}
Use fields and Arrow functions
class MyClass3 implements MyInterface {
bar = 'my bar';
foo = () => {
console.log(this.bar);
};
}
Why does it work?
Arrow functions are automatically bound (reference)
Problems:
- Problems with arrow functions in class fields
- Not Mockable
- Inheritance won't work as expected
- Memory: Functions will turn to per-instance functions (i.e. each object has their own function object)function implementation (as opposed to one implemenation for all objects).