Memoizee instance method in node Class - javascript

I'm seeking an elegant way to memoize a class function using Memoizee package.
Outside a class, you can go about this trivially:
const memoize = require('memoizee')
const myFunc = memoize(function myfunc(){ ... })
but inside a class block, this won't work:
class foo {
constructor(){ ... }
// Without memoization you would do:
myFunc(){ ... }
// Can't do this here:
myFunc = memoize(function myfunc(){ ... })
}
I can think of creating it in the constructor using this. syntax, but this will result in a less uniform class definition, as non-memoized methods will be declared outside the constructor:
class foo {
constructor(){
// Inside for memoized:
this.myFunc = memoize(function myfunc(){ ... })
}
// Outside for non-memoized:
otherFunc(){ ... }
}
How would you wrap an instance method?

It's possible to overwrite own method definition in the constructor
class Foo {
constructor() {
this.bar = _.memoize(this.bar);
}
bar(key) {
return `${key} = ${Math.random()}`;
}
}
const foo = new Foo();
console.log(foo.bar(1));
console.log(foo.bar(1));
console.log(foo.bar(2));
console.log(foo.bar(2));
// Output:
1 = 0.6701435727286942
1 = 0.6701435727286942
2 = 0.38438568145894747
2 = 0.38438568145894747

Depending on the way you run your code and whether or not you're using transpilation steps, maybe you can use the memoized-class-decorator with:
class foo {
constructor () { ... }
// Without memoization:
myFunc () { ... }
// With memoization:
#memoize
myFunc () { ... }
}

There is a dedicated handling for methods in memoizee. See: https://github.com/medikoo/memoizee#memoizing-methods
Still it won't work with native class syntax, best you can do at this point is something as:
const memoizeMethods = require('memoizee/methods');
class Foo {
// .. non memoized definitions
}
Object.defineProperties(Foo.prototype, memoizeMethods({
// Memoized definitions, need to be provided via descriptors.
// To make it less verbose you can use packages as 'd':
// https://www.npmjs.com/package/d
myFunc: {
configurable: true,
writable: true,
enumerable: false,
value: function () { ... }
}
});

Related

How do I modify a class method?

I have code that looks like this (simplified)
class foo {
constructor() {
this.bar()
}
bar() {
// other code ...
console.log('original');
}
}
new foo(); // output: "original"
I would look to add more code inside the bar() method without affecting what already exists.
So something like this
bar() {
// original code fires
// then...
console.log('new stuff');
}
and the final output would look like
original
new stuff
A couple of notes:
I don't want to override bar() - I just want to add to it.
I don't to call bar() - it's called upstream and I just want to piggyback on that call.
I've been reading about super but that seems to be more related to sub-classes (which I don't want to create)
So I guess here's my question:
Is it possible to add to the contents of a class method in the way I described above?
This is almost certainly an XY problem, but the literal solution of your question:
class Foo {
constructor() {
this.bar()
}
bar() {
// other code ...
console.log('original');
}
}
new Foo(); // output: "original"
const addedLine = 'console.log("new stuff");\n';
const source = Foo.prototype.bar.toString();
let [_, args, body] = source.match(/^.*?\((.*?)\)\s*{([^]*)}\s*$/);
body += addedLine;
args = args.split(/\s*,\s*/);
Foo.prototype.bar = Function(...args, body);
new Foo(); // output: "original", "new stuff"
Warning: This is extremely hacky! Outside of proof of concept, you should not consider doing this in production code.
A bit less hacky solution, that mimics super (and is more restricted than the black magic above):
class Foo {
constructor() {
this.bar()
}
bar() {
// other code ...
console.log('original');
}
}
new Foo(); // output: "original"
(() => {
const oldBar = Foo.prototype.bar;
Foo.prototype.bar = function() {
oldBar.apply(this, arguments);
console.log("new stuff");
}
})();
new Foo(); // output: "original", "new stuff"
EDIT: It bugs me when classes start with a lowercase.
I recommend you add a new method inside the class and that method can use other methods in that class eg
foo.prototype.modifiedBoo = () => {
console.log("I'm modified!")
};
console.log(new foo.modifiedBoo())
// I'm modified!
If you cannot use inheritance, maybe you can use composition:
class foo {
constructor() {
this.bar()
}
bar() {
// other code ...
console.log('original');
}
}
new foo(); // output: "original"
class FooWrapper {
f;
constructor() {
this.bar();
}
bar() {
console.log("other code...");
this.f = new foo();
}
}
new FooWrapper();
class WhateverWrapper {
whatever;
constructor(wrapped) {
this.bar();
this.whatever = new wrapped();
}
bar() {
console.log("other code...");
}
}
new WhateverWrapper(foo);

Call all methods in a class when an instance is created

Let's say I have a class that looks like this
class Foo {
bar() {
console.log('bar');
}
baz() {
console.log('baz');
}
// other methods...
}
and I want all of its methods to be unconditionally called whenever a new instance is created. How would I go about that?
I'm currently doing it by calling each method in the class constructor method
class Foo {
constructor() {
this.bar();
this.baz();
// other methods
}
bar() {
console.log('bar');
}
baz() {
console.log('baz');
}
// other methods...
}
here's a snippet of that
class Foo {
constructor() {
this.bar();
this.baz();
// other methods
}
bar() {
console.log('bar');
}
baz() {
console.log('baz');
}
// other methods...
}
new Foo();
Is there a better way to do this?
This seems to be the closest to what I want to achieve. It works by ditching class methods and using IIFEs in classProperties instead.
So something like this
class Foo {
bar = (() => {
console.log('bar');
})()
baz = (() => {
console.log('baz');
})()
}
new Foo();
and it works well on Chrome but the majority of other browsers don't yet have support for classProperties since they were added in ES9
My question is:
If I have a class with a number of methods in it, can I call all of them whenever a new instance of that class is created without having to call each individual method in the constructor method of that class?
In the context of this question, you can completely ignore the need to pass parameters to the methods. I don't need to pass any parameters to any of the class methods.
If the order in which you call the methods is not important, you can iterate dynamically over the properties of your created instance with Object.getOwnPropertyNames method:
class Foo {
constructor() {
return Object.getOwnPropertyNames (Object.getPrototypeOf (this))
.filter(propName => (propName !== 'constructor' && typeof this[propName] === 'function'))
.forEach(propName => this[propName]());
}
bar() {
console.log('bar');
}
baz() {
console.log('baz');
}
// other methods...
}
You can declare function this way:
bar = function() {
console.log('bar');
};
And iterate values of new instance, check for functions and call them:
class Foo {
constructor() {
Object.values(this).forEach(value => {
if (typeof value === 'function') {
value();
}
});
}
nonFunctionProp = 'nfp';
bar = function() {
console.log('bar');
};
baz = function() {
console.log('baz');
};
// other methods...
}
new Foo();

How do you define a function of a class in another Javascript file using Node require?

Let's say I have a class A defined in its own JavaScript file, like this:
A.js
class A {
constructor() {
// blah blah blah
}
func() {
// a long function
}
}
If I have a function (e.g. func()) that I want to be contained in its own file (for organizational purposes), how would I accomplish this?
What I want is something like this:
A.js
class A {
constructor() {
this.func = {};
}
} exports.A = A;
ADefinition.js
var A = require('./A.js');
A.func = () => {
// a long function
}
This obviously doesn't work, but how would one accomplish this?
Classes are mostly just syntax sugar. In ES5, you define prototype functions by assigning to the prototype:
function A() {
}
A.prototype.func = function() {
}
Classes can work the same way:
var A = require('./A.js');
A.prototype.func = () => {
// a long function
}
Though, note that if you use an arrow function, you won't have access to the instance - you may well need a full-fledged function instead:
A.prototype.func = function() {
// a long function
};
Also, personally, I think I'd prefer to put func on the class next to the class definition for code clarity, rather than running a module that performs side effects (like your current code is attempting to do), for example:
const func = require('./func.js');
class A {
constructor() {
}
}
A.prototype.func = func;

Overriding methods from JavaScript function-based classes in TypeScript

I'm trying to override a object method defined in JavaScript (ES5) function-based class:
var JSClass = function() {
this.start = function() {
console.log('JSClass.start()');
}
}
Then calling start() method prints as expected:
let o1 = new JSClass();
o1.start();
// prints: JSClass.start()
However, if I try to extend this object with a TypeScript class such as:
class TSClass extends JSClass {
start() {
super.start();
console.log('TSClass.start()');
}
otherStart() {
this.start();
console.log('TSClass.otherStart()');
}
}
... then TSClass::start() is never called. Only the start() defined in JSClass.
let o2 = new TSClass();
o2.start();
o2.otherStart();
This prints just:
JSClass.start()
JSClass.start()
TSClass.otherStart()
I'd expect to print:
// by calling: o2.start();
JSClass.start()
TSClass.start()
// by calling: o2.otherStart();
JSClass.start()
TSClass.start()
TSClass.otherStart()
Is this by design? Then how can I extend ES5 object methods with TypeScript?
See live demo: https://jsfiddle.net/martinsikora/2sunkmq7/
Edit: I ended up using this.
class TSClass extends JSClass {
constructor() {
var oldStart = this.start;
this.start = () => {
oldStart.call(this);
console.log('TSClass.start()');
}
}
// ...
}
Now it works as expected.
Your problem is that you're adding the start method to JSClass as a class member instead of class method.
In order to make it a method you need to add it to the prototype:
var JSClass = function () {}
JSClass.prototype.start = function () {
console.log('JSClass.start()');
}
Then this:
let o2 = new TSClass();
o2.start();
o2.otherStart();
Results in:
JSClass.start()
TSClass.start()
JSClass.start()
TSClass.start()
TSClass.otherStart()

Split a Javascript class (ES6) over multiple files?

I have a Javascript class (in ES6) that is getting quite long. To organize it better I'd like to split it over 2 or 3 different files. How can I do that?
Currently it looks like this in a single file:
class foo extends bar {
constructor(a, b) {} // Put in file 1
methodA(a, b) {} // Put in file 1
methodB(a, b) {} // Put in file 2
methodC(a, b) {} // Put in file 2
}
Thanks!
When you create a class
class Foo extends Bar {
constructor(a, b) {
}
}
you can later add methods to this class by assigning to its prototype:
// methodA(a, b) in class Foo
Foo.prototype.methodA = function(a, b) {
// do whatever...
}
You can also add static methods similarly by assigning directly to the class:
// static staticMethod(a, b) in class Foo
Foo.staticMethod = function(a, b) {
// do whatever...
}
You can put these functions in different files, as long as they run after the class has been declared.
However, the constructor must always be part of the class declaration (you cannot move that to another file). Also, you need to make sure that the files where the class methods are defined are run before they are used.
Here's my solution. It:
uses regular modern classes and .bind()ing, no prototype. (EDIT: Actually, see the comments for more on this, it may not be desirable.)
works with modules. (I'll show an alternative option if you don't use modules.)
supports easy conversion from existing code.
yields no concern for function order (if you do it right).
yields easy to read code.
is low maintenance.
unfortunately does not play well with static functions in the same class, you'll need to split those off.
First, place this in a globals file or as the first <script> tag etc.:
BindToClass(functionsObject, thisClass) {
for (let [ functionKey, functionValue ] of Object.entries(functionsObject)) {
thisClass[functionKey] = functionValue.bind(thisClass);
}
}
This loops through an object and assigns and binds each function, in that object, by its name, to the class. It .bind()'s it for the this context, so it's like it was in the class to begin with.
Then extract your function(s) from your class into a separate file like:
//Use this if you're using NodeJS/Webpack. If you're using regular modules,
//use `export` or `export default` instead of `module.exports`.
//If you're not using modules at all, you'll need to map this to some global
//variable or singleton class/object.
module.exports = {
myFunction: function() {
//...
},
myOtherFunction: function() {
//...
}
};
Finally, require the separate file and call BindToClass like this in the constructor() {} function of the class, before any other code that might rely upon these split off functions:
//If not using modules, use your global variable or singleton class/object instead.
let splitFunctions = require('./SplitFunctions');
class MySplitClass {
constructor() {
BindToClass(splitFunctions, this);
}
}
Then the rest of your code remains the same as it would if those functions were in the class to begin with:
let msc = new MySplitClass();
msc.myFunction();
msc.myOtherFunction();
Likewise, since nothing happens until the functions are actually called, as long as BindToClass() is called first, there's no need to worry about function order. Each function, inside and outside of the class file, can still access any property or function within the class, as usual.
I choose to have all privte variables/functions in an object called private, and pass it as the first argument to the external functions.
this way they have access to the local variables/functions.
note that they have implicit access to 'this' as well
file: person.js
const { PersonGetAge, PersonSetAge } = require('./person_age_functions.js');
exports.Person = function () {
// use privates to store all private variables and functions
let privates={ }
// delegate getAge to PersonGetAge in an external file
// pass this,privates,args
this.getAge=function(...args) {
return PersonGetAge.apply(this,[privates].concat(args));
}
// delegate setAge to PersonSetAge in an external file
// pass this,privates,args
this.setAge=function(...args) {
return PersonSetAge.apply(this,[privates].concat(args));
}
}
file: person_age_functions.js
exports.PersonGetAge =function(privates)
{
// note: can use 'this' if requires
return privates.age;
}
exports.PersonSetAge =function(privates,age)
{
// note: can use 'this' if requires
privates.age=age;
}
file: main.js
const { Person } = require('./person.js');
let me = new Person();
me.setAge(17);
console.log(`I'm ${me.getAge()} years old`);
output:
I'm 17 years old
note that in order not to duplicate code on person.js, one can assign all functions in a loop.
e.g.
person.js option 2
const { PersonGetAge, PersonSetAge } = require('./person_age_functions.js');
exports.Person = function () {
// use privates to store all private variables and functions
let privates={ }
{
// assign all external functions
let funcMappings={
getAge:PersonGetAge,
setAge:PersonSetAge
};
for (const local of Object.keys(funcMappings))
{
this[local]=function(...args) {
return funcMappings[local].apply(this,[privates].concat(args));
}
}
}
}
You can add mixins to YourClass like this:
class YourClass {
ownProp = 'prop'
}
class Extension {
extendedMethod() {
return `extended ${this.ownProp}`
}
}
addMixins(YourClass, Extension /*, Extension2, Extension3 */)
console.log('Extended method:', (new YourClass()).extendedMethod())
function addMixins() {
var cls, mixin, arg
cls = arguments[0].prototype
for(arg = 1; arg < arguments.length; ++ arg) {
mixin = arguments[arg].prototype
Object.getOwnPropertyNames(mixin).forEach(prop => {
if (prop == 'constructor') return
if (Object.getOwnPropertyNames(cls).includes(prop))
throw(`Class ${cls.constructor.name} already has field ${prop}, can't mixin ${mixin.constructor.name}`)
cls[prop] = mixin[prop]
})
}
}
TypeScript Solution
foo-methods.ts
import { MyClass } from './class.js'
export function foo(this: MyClass) {
return 'foo'
}
bar-methods.ts
import { MyClass } from './class.js'
export function bar(this: MyClass) {
return 'bar'
}
class.ts
import * as barMethods from './bar-methods.js'
import * as fooMethods from './foo-methods.js'
const myClassMethods = { ...barMethods, ...fooMethods }
class _MyClass {
baz: string
constructor(baz: string) {
this.baz = baz
Object.assign(this, myClassMethods);
}
}
export type MyClass = InstanceType<typeof _MyClass> &
typeof myClassMethods;
export const MyClass = _MyClass as unknown as {
new (
...args: ConstructorParameters<typeof _MyClass>
): MyClass;
};
My solution is similar to the one by Erez (declare methods in files and then assign methods to this in the constructor), but
it uses class syntax instead of declaring constructor as a function
no option for truly private fields - but this was not a concern for this question anyway
it does not have the layer with the .apply() call - functions are inserted into the instance directly
one method per file: this is what works for me, but the solution can be modified
results in more concise class declaration
1. Assign methods in constructor
C.js
class C {
constructor() {
this.x = 1;
this.addToX = require('./addToX');
this.incX = require('./incX');
}
}
addToX.js
function addToX(val) {
this.x += val;
return this.x;
}
module.exports = addToX;
incX.js
function incX() {
return this.addToX(1);
}
module.exports = incX;
2. Same, but with instance fields syntax
Note that this syntax is a Stage 3 proposal as of now.
But it works in Node.js 14 - the platform I care about.
C.js
class C {
x = 1;
addToX = require('./addToX');
incX = require('./incX');
}
Test
const c = new C();
console.log('c.incX()', c.incX());
console.log('c.incX()', c.incX());

Categories

Resources