Call all methods in a class when an instance is created - javascript

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();

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);

How to emulate Javascript `super` so `new.target` is defined?

I'm attempting to emulate this javascript without using the ES6 keywords (e.g. without class, super or extends):
class Foo {
constructor() {
if (!new.target)
console.log('Foo() must be called with new');
}
}
class Bar extends Foo {
constructor() {
super(...arguments);
}
}
var bar = new Bar();
var barIsFoo = bar instanceof Foo;
console.log(barIsFoo); // true
I got this far, but they're not equivalent. The following throws (I log instead) while the latter does not:
function Foo() {
if (!new.target)
console.log('Foo() must be called with new');
}
function Bar() {
Foo.apply(this, arguments)
}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.constructor = Bar;
var bar = new Bar();
var barIsFoo = bar instanceof Foo;
console.log(barIsFoo);
So, how do I emulate providing a value for new.target when I call into Foo from Bar?
So, seems there's no apply or call that allows passing the new.target. I suppose that would defeat the purpose of new.target (though the fact that everything in JS is public really appealed to me).
So to emulate in ES5 we'd need to add something.
One solution in an answer below allocates a new object.
This solution adds new functions construct which can be chained as usual in ES5 and leave the function itself free to do nothing more than check if it's being used as a constructor.
function Foo() {
if (!new.target)
throw 'Foo() must be called with new';
console.log('Foo new check');
Foo.prototype.construct.apply(this, arguments);
}
Foo.prototype.construct = function() {
console.log('Foo construction logic');
}
function Bar() {
if (!new.target)
throw 'Bar() must be called with new';
console.log('Bar new check');
Bar.prototype.construct.apply(this, arguments);
}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.constructor = Bar;
Bar.prototype.construct = function() {
// super()
Foo.prototype.construct.apply(this, arguments);
console.log('Bar construction logic');
}
var bar = new Bar();
var barIsFoo = bar instanceof Foo;
console.log(barIsFoo);
Bottom line, it appears ES6 features not not just syntactic sugar over ES5. Of course, they could just add a Function.prototype.super(target, arguments, newTarget) and then we could just use that. I hope they do!
Only super can call a function in Javascript and have this not be available immediately. So super is unique. And super can only be called in the context of a constructor which can only be used in the context of a class. Sooo all those key words are necessary to make super work. So Javascript has introduced a very specific object oriented feature. Looks like building a language on top of the idea of a "prototype" has it's limits.
Which is a shame...
I wonder why, all of a sudden, javascript decided to enforce this one invariant. That this is not available before the super call. Why not just make super a short hand for BaseType.prototype.constructor.call(this, ...). Why not allow it to be invoked more than once? We can blow our toes off so many other ways in Javascript, why start enforcing things now?
Well, anyway...
So, double bottom line, there exists a early bound Javascript call super that has no late bound equivalent (unlike, for example, foo.bar() which can be called late(r)bound via bar.call('foo')).
Use Object.assign to assign the parent constructor's new Foo(...arguments) to the instance:
function Foo(arg) {
if (!new.target)
throw 'Foo() must be called with new';
this.arg = arg;
this.fooProp = 'fooProp';
}
function Bar() {
Object.assign(
this,
new Foo(...arguments)
);
this.barProp = 'barProp';
}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.constructor = Bar;
var bar = new Bar('abc');
var barIsFoo = bar instanceof Foo;
console.log(barIsFoo);
console.log(bar);
But new Foo(...arguments) is ES6 syntax. To translate that to ES5, instead use
new (Function.prototype.bind.apply(Foo, [null, ...arguments]))()
(which takes care of the new part), which again transpiles down to
new (Function.prototype.bind.apply(Foo, [null].concat(Array.prototype.slice.call(arguments))))()
function Foo(arg) {
if (!new.target)
throw 'Foo() must be called with new';
this.arg = arg;
this.fooProp = 'fooProp';
}
function Bar() {
Object.assign(
this,
new (Function.prototype.bind.apply(Foo, [null].concat(Array.prototype.slice.call(arguments))))()
);
this.barProp = 'barProp';
}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.constructor = Bar;
var bar = new Bar('abc');
var barIsFoo = bar instanceof Foo;
console.log(barIsFoo);
console.log(bar);
ES6 class syntax is not syntax sugar for ES5, but it is pretty much syntax sugar for other ES6 functionality.
class Bar extends Foo {
constructor() {
super(...arguments);
}
}
is pretty similar to if you did
let Bar = function(...args) {
const _this = Reflect.construct(Object.getPrototypeOf(Bar), args, new.target);
return _this;
};
Object.setPrototypeOf(Bar, Foo);
Object.setPrototypeOf(Bar.prototype, Foo.prototype);
where Reflect.construct constructs and object with a given new.target value and calls a given constructor with a set of arguments.
To write abstract classes, you need to make sure that the constructor of the parent class is called in the context of the prototype of the called class.
To do this, ES6 introduced "Reflect.construct".
For ES5, you can implement your own "crutch".
// Reflect.construct simulation for ES5
class Reflect2 {
static construct(TargetClass,args,ProtoClass){
let oldProto= TargetClass.prototype;
let desc=Object.getOwnPropertyDescriptor(TargetClass,'prototype');
if(desc.hasOwnProperty('value') && desc.writable===false){
if(desc.configurable===true){
desc.writable=true;
Object.defineProperty(TargetClass,'prototype',desc);
}
}
TargetClass.prototype=ProtoClass.prototype;
let self=new (Function.prototype.bind.apply(TargetClass,args));
TargetClass.prototype=oldProto;
return self;
}
}
function ParentClass(){
}
ParentClass.prototype.constructor=ParentClass;
function MyClass(){
if(new.target===undefined){throw Error();}
let self=Reflect2.construct (ParentClass,[],new.target);
// your code
return self;
}
MyClass.prototype=Object.create(ParentClass.prototype,{
constructor:{
value:MyClass
}
});
let a=new MyClass();
console.log(Object.getPrototypeOf(a)===MyClass.prototype);
"super" in ES6 is the same "this", but in the context of the parent prototype. For example, when we bind an object to a function via "bind ()", here we bind object "this" only to the prototype object.
"super" can be implemented in 2 ways:
through prototype scanning, and binding to methods and reactive properties of the 'this' object;
via Proxy object
Emulation 'super' in ES6
via Proxy object
/**
* #param {object} self - this object
* #param {function} ProtoClass - the class to observe.
Usually the current class for which methods are being written.
* #param {function} [new_target] used in the constructor.
If not passed, then the default will be the prototype constructor for "self"
(Object.getPrototyprOf(self).constructor).
* #param {boolean} [bindSelf] - if true, then the methods and reactive properties
of the parent will work in the context of self, if false, then the methods and
reactive properties will work within the framework of the object in which they
are called (i.e. within the Proxy object).
*/
function Super(self,ProtoClass,new_target,bindSelf=false){
let parentProto=Object.getPrototypeOf(ProtoClass.prototype);
let descs={};
// collect descriptors of all prototypes
do {
let buf=Object.getOwnPropertyDescriptors(parentProto);
for(let prop of Object.keys(buf)){
if(!Object.prototype.hasOwnProperty.call(descs,prop)){
descs[prop]=buf[prop];
}
}
parentProto=Object.getPrototypeOf(parentProto);
}while (parentProto!==null);
// we define the control object
let answer={};
let new_obj=function (...args){
let ParentClass=Object.getPrototypeOf(ProtoClass.prototype).constructor;
if(ParentClass===Object || ProtoClass===ParentClass){
return self;
}
new_target=new_target??Object.getPrototypeOf(self).constructor;
return Reflect.construct(ParentClass,args,new_target);
}
let parent=new Proxy(self,{
get(target,prop){
let desc=descs[prop];
if(desc===undefined){
return;
}
if(desc.hasOwnProperty('value')){
if(bindSelf && typeof desc.value==='function'){
return desc.value.bind(target);
}
return desc.value;
} else
if(desc.hasOwnProperty('get')){
return desc.get.call(target);
}
},
set(target,prop,value){
let desc=descs[prop];
if(desc===undefined || desc.hasOwnProperty('value')){
target[prop]=value;
return true;
}
if(desc.hasOwnProperty('set')){
desc.set.call(target,value);
return true;
}
}
});
Object.defineProperties(answer,{
new:{
value:new_obj
},
parent:{
value:parent
}
});
return answer;
}
How to use
function A(...args){
}
A.prototype.method=function(){
console.log('hello',this);
return this;
}
A.prototype.constructor=A;
function B(...args){
if(new.target===undefined){throw Error();}
let _super=Super(this,B,new.target);
let self= _super.new(...args);
let b=_super.parent.method(); // return proxy object
/*
in this case, all execution inside the method will occur as if this has a parent prototype.
*/
console.log(b===this);// false
return self;
}
B.prototype=Object.create(A.prototype);
B.prototype.constructor=B;
B.prototype.method=function(){
console.log('bay');
return this;
}
let b=new B();
or
function A(...args){
}
A.prototype.method=function(){
console.log('hello',this);
return this;
}
A.prototype.constructor=A;
function B(...args){
if(new.target===undefined){throw Error();}
/*
we want the methods to execute against the "this" context
and not in the "new Proxy ('this', {})" context
*/
let _super=Super(this,B,new.target,true);// arg[3] true
let self= _super.new(...args);
let b=_super.parent.method(); // return this object
console.log(b===this);// true
return self;
}
B.prototype=Object.create(A.prototype);
B.prototype.constructor=B;
B.prototype.method=function(){
console.log('bay');
return this;
}
let b=new B();
for ES5 Proxies can be replaced with a regular object, where “this” through “bind” is bound to methods.
function Super(self,ProtoClass,new_target){
let parentProto=Object.getPrototypeOf(ProtoClass.prototype);
let parent={};
// properties all prototypes
do {
let buf=Object.getOwnPropertyDescriptors(parentProto);
for(let prop of Object.keys(buf)){
if(!Object.prototype.hasOwnProperty.call(parent,prop)){
let desc=buf[prop];
if(buf[prop].hasOwnProperty('get') ){
desc.get=buf[prop].get.bind(self);
}
if(buf[prop].hasOwnProperty('set') ){
desc.set=buf[prop].set.bind(self);
}
if(buf[prop].hasOwnProperty('value') ){
if (typeof buf[prop].value === 'function') {
desc.value=buf[prop].value.bind(self);
} else{
delete desc.value;
delete desc.writable;
desc.get=function (){
return buf[prop];
};
desc.set=function(v){
self[prop]=v;
};
}
}
Object.defineProperty(parent,prop,desc);
}
}
parentProto=Object.getPrototypeOf(parentProto);
} while (parentProto!==null);
// we define the control object
let answer={};
let new_obj=function (){
let ParentClass=Object.getPrototypeOf(ProtoClass.prototype).constructor;
if(ParentClass===Object || ProtoClass===ParentClass){
return self;
}
new_target=new_target??Object.getPrototypeOf(self).constructor;
// We described the "Reflect.construct method" above. see Reflect2.construct
return Reflect.construct(ParentClass,Array.prototype.slice.call(arguments),new_target);
}
Object.defineProperties(answer,{
new:{
value:new_obj
},
parent:{
value:parent
}
});
return answer;
}
Example
function A(...args){
}
A.prototype.method=function(){
console.log('hello',this);
return this;
}
A.prototype.constructor=A;
function B(...args){
if(new.target===undefined){throw Error();}
let _super=Super(this,B,new.target);
let self= _super.new(...args);
let b=_super.parent.method();
console.log(b===this);
return self;
}
B.prototype=Object.create(A.prototype);
B.prototype.constructor=B;
B.prototype.method=function(){
console.log('bay');
return this;
}
let b=new B();
The purpose of using if (!new.target) is to check if the Foo is called with new(as a constructor), in which case we can just use if(this instanceof Foo) instead.
this could be an instance of Foo or Bar, and whether you call new Foo() or Foo.apply(this), the this instanceof Foo can always return true while it will return false if you just call Foo()(without new or apply/call).
Try this:
function Foo() {
if (!this instanceof Foo)
console.log('Foo() must be called with new');
}
function Bar() {
Foo.apply(this, arguments)
}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.constructor = Bar;
var bar = new Bar();
var barIsFoo = bar instanceof Foo;
console.log(barIsFoo);

Publish subscriber pattern using instance based decorators 'this' is always undefined

class Observable {
constructor() {
this.handlers = [];
}
publish(value) {
this.handlers.forEach(handler => {
handler(value);
});
}
subscribe(callback) {
this.handlers.push(callback);
}
}
const concreteObserver = new Observable();
function Subscribe(observable) {
return function functionDescriptor(target, propertyKey, descriptor) {
observable.subscribe(target[propertyKey]);
return descriptor;
}
}
class MyClass {
constructor(){
this.x = 5;
}
#Subscribe(concreteObserver)
subsribeToValue(value) {
console.log(this.x); // undefined
}
}
As you can see, the subscribe function is called each time, someone calls concreteObserver.publish() however, when you call observable.subscribe(target[propertyKey]); then 'this' becomes undefined.
I also tried overriding the descriptor getter, and calling that one, but i still get undefined. On classes i was able to wrap a function by calling target.prototype.functionName.
This works when i know what the function name will be called, but the function name for #Subscribe can be arbitrary, so i can't use it on a class level decorator unless i use Reflection to detect all the annotations of the class.
EDIT
Tried so far
observable.subscribe(target[propertyKey].bind(this));
which returns undefined, subscribe has the right context in this case.
observable.subscribe(data => descriptor.value.apply(this, data)); also has 'this' as undefined
descriptor.value = function(){
console.log(this); //undefined
}
descriptor.get = function(){
console.log(this); //undefined
}
The solution i came up with. Since it is only possible to get the instance of a class in the class decorator, then that is where this can be used properly, in the the subscribe function i tell what function i should subscribe to, then in the ClassDecorator i iterate through each method to determine if they have __subscribeFunction in their prototype and thus subscribe to the method while binding instance
class Observable {
constructor() {
this.handlers = [];
}
publish(value) {
this.handlers.forEach(handler => {
handler(value);
});
}
subscribe(callback) {
this.handlers.push(callback);
}
}
const concreteObserver = new Observable();
function ClassDecorator(target) {
const originalTarget = target;
const Override = function (...args) {
const instance = originalTarget.apply(this, args);
Object.values(instance.__proto__).forEach(method => {
const observableFunction = method.prototype.__subscribeFunction;
if (observableFunction) {
observableFunction.subscribe(method.bind(instance));
}
});
return instance;
};
Override.prototype = originalTarget.prototype;
customElements.define(elementName, target);
return Override;
}
function Subscribe(observable) {
return function functionDescriptor(target, propertyKey, descriptor) {
target[propertyKey].prototype.__subscribeFunction = observable;
}
}
#ClassDecorator
class MyClass {
constructor(){
this.x = 5;
}
#Subscribe(concreteObserver)
subsribeToValue(value) {
console.log(this.x); // 5
}
}
This doesn't work because the decorator is called when the class itself is constructed, but before any instance is created. Since there's no instance, there can't be a this – you only have access to the prototype, but class properties aren't on the prototype (unlike methods).
You can verify this using this example:
function Example() {
console.log("#Example initialized");
return function exampleDescriptior(target, propertyKey, descriptor) {
console.log("#Example called");
}
}
console.log("Before declaring class");
class Test {
#Example()
public test() {}
}
console.log("After declaring class");
console.log("Before creating instance");
const test = new Test();
console.log("After creating instance");
console.log("Before calling method");
test.test();
console.log("After calling method");
which yields the output
Before declaring class
#Example initialized
#Example called
After declaring class
Before creating instance
After creating instance
Before calling method
After calling method
That said, what you can do is write another decorator applied on, say, class level which proxies the constructor. If your #Subscribe annotation stores some meta-data on the prototype, the class decorator could then look for it and do the actual wiring. So getting something like
#AutoSubscribe()
class MyClass {
#Subscribe(observer)
subscribe(value) {
console.log(this.x);
}
}
to work should be possible. In fact, you could maybe even get rid of the second decorator by proxying the constructor from the #Subscribe decorator, but you'd still have to store metadata that you can look through during instantiation.

Why do we do this.xyz inside a function

I was trying to understand that why do we do this.xyz inside a function?
Like consider this function which we are calling like using foo()
function foo() {
this.value = "Shivom"
console.log(this.value) //Shivom
console.log(value) //Shivom
}
And alternate for this could be
function foo() {
value = "shivom"
console.log(this.value) //shivom
console.log(value) //shivom
}
Similarly, When In react I was using websocket someone asked me to do
class cryptoTicker extends Component {
componentDidMount() {
this.socket = openSocket('https://coincap.io');
let updateCoinData = [...this.props.cryptoLoaded];
this.socket.on('trades', (tradeMsg) => {
componentWillUnmount() {
this.socket.disconnect();
}
Instead of doing something like this
var socket;
class cryptoTicker extends Component {
componentDidMount() {
socket = openSocket('https://coincap.io');
let updateCoinData = [...this.props.cryptoLoaded];
socket.on('trades', (tradeMsg) => {
componentWillUnmount() {
socket.disconnect();
}
[Question:] So when and why do we use this.xyz inside a function? if someone can please explain me using the given two example above?
Well, in a nutshell:
var foo;
function bar(baz) {
foo = baz;
console.log(foo);
}
You can only have one foo value ever in your program this way. It's a singleton/global value.
class Foo {
constructor() {
this.foo = undefined;
}
bar(baz) {
this.foo = baz;
console.log(this.foo);
}
}
let a = new Foo;
let b = new Foo;
a.bar(42);
b.bar('fortytwo');
Using classes and object and setting properties on individual objects (this) allows you to have multiple independent instances of an object/value. That's the basis of OOP.

Memoizee instance method in node Class

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 () { ... }
}
});

Categories

Resources