Calling class instance as a function in JavaScript - javascript

Assuming I have a class
class foo {
constructor() {
this._pos = 0;
}
bar(arg) {
console.log(arg);
}
}
const obj = new foo();
How do I make it possible to call:
let var1 = obj('something');

You can make a callable object by extending the Function constructor, though if you want it to access the instance created, you'll actually need to create a bound function in the constructor that binds the instance to a function that is returned.
class foo extends Function {
constructor() {
super("...args", "return this.bar(...args)");
this._pos = 0;
return this.bind(this);
}
bar(arg) {
console.log(arg + this._pos);
}
}
const obj = new foo();
let var1 = obj('something ');

Related

How to update a variable of an object of a class in javascript by passing an callback to another function in a separate object

How can I update a variable of an object of a particular class by passing an callback to another function inside a separate object. Here in the code in the class Example when I make an object of it and then call its callOtherMethod the console.log made by the updateArr() method shows this.arr as undefined. This is an example of the problem that I faced. Thank you for help
class Example{
constructor(){
this.arr = [];
this.index = 2;
}
updateArr(str){
this.arr[this.index] = str;
console.log(this.arr)
}
callOtherMethod(){
this.index = 1;
anotherObject.method(this.updateArr)
}
}
anotherObject= {
method : (callback) =>{
callback('Hello')
}
}
const ex = new Example();
ex.callOtherMethod()
//logs this.arr undefined
When you pass this.updateArr and execute it in your method function:
method : (callback) =>{
callback('Hello')
}
... the this that gets bound to updateArr gets lost, as the calling context to callback is not specified.
To fix this, you can pass a special version of your this.updateArr to your anotherObject.method() method, which has its this explicitly bound to it using the .bind() function. The .bind() method will return a new function with the this being bound to be the first argument that you pass.
See example below:
class Example {
constructor() {
this.arr = [];
this.index = 2;
}
updateArr(str) {
this.arr[this.index] = str;
console.log(this.arr)
}
callOtherMethod() {
this.index = 1;
// pass a function with the `this` being bound to the current object
anotherObject.method((...args) => this.updateArr(...args))
}
}
anotherObject = {
method: (callback) => {
callback('Hello')
}
}
const ex = new Example();
ex.callOtherMethod();
Alternatively, you can use:
anotherObject.method((...args) => this.updateArr(...args))
This will pass a function into arnotherObject.method() which when executed will run:
this.updateArr(...args)
since the this here refers to your object instance (as arrow function's don't have their own this binding) you will be able to call your instance's updateArr method.
You could either use the traditional approach of binding the object to the function as suggested in another answer.
Or also pass the function name as a string along with the original object. And then access the function using the property of the object:
class Example{
constructor(){
this.arr = [];
this.index = 2;
}
updateArr(str){
this.arr[this.index] = str;
console.log(this.arr)
}
callOtherMethod(){
this.index = 1;
anotherObject.method(this, "updateArr")
}
}
anotherObject= {
method : (obj, callback) =>{
obj[callback]('Hello');
}
}
const ex = new Example();
ex.callOtherMethod();

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.

extending singleton class in es6

I need to extend a singleton class in JavaScript .
The problem that I am facing is that I get the class instance which I am extending from instead of only getting the methods of the class.
I have tried to remove super to not get the instance but then I got an error
Must call super constructor in derived class before accessing 'this' or returning from derived constructor
Code example:
let instanceA = null;
let instanceB = null;
class A {
constructor(options) {
if (instanceA === null) {
this.options = options;
instanceA = this;
}
return instanceA;
}
}
class B extends A {
constructor(options) {
if (instanceB === null) {
super()
console.log('real class is ' + this.constructor.name)
this.options = options
instanceB = this;
}
return instanceB;
}
}
const a = new A({
i_am_a: "aaaaaa"
});
const b = new B({
i_am_b: "bbbbbb"
}) // this change a
console.log(b.options)
console.log(a.options)
So, first of all there's a misconception here:
I have tried to remove super to not get the instance but then I got an error
super() calls the parent class' constructor on the created instance of the child class (i.e. what this is referencing). It does not return a parent class instance. See here for more information.
So, calling super() does not violate the singleton property of the parent class at all. It may well be only constructed a single time if implemented correctly.
With that in mind, you should improve your code a little bit.
A sensible change would be to remove the instance management from the constructors. One solution would be to use static constructors which either create the singleton if no instance exists or return the created instance.
Another one is to drop the arguments to the singleton class constructors. It doesn't really make sense to pass arguments to a class which is supposed to be instantiated once (you're never gonna do anything with the constructor arguments again). You could just make the arguments properties of the singleton right away. Here's a SO answer supporting this point for Java singletons.
A complete example with static constructors and without arguments looks like this:
let instanceA = null;
let instanceB = null;
let counters = { A: 0, B: 0 }; // count class instantiations
class A {
static getInstance() {
if (instanceA === null) {
instanceA = new A();
}
return instanceA;
}
whoami() {
const name = this.constructor.name;
return `${name} #${counters[name]}`;
}
constructor() {
counters[this.constructor.name] += 1;
}
}
class B extends A {
static getInstance() {
if (instanceB === null) {
instanceB = new B();
}
return instanceB;
}
constructor() {
super();
}
}
const a1 = A.getInstance();
const a2 = A.getInstance();
const a3 = A.getInstance();
const b1 = B.getInstance();
const b2 = B.getInstance();
const b3 = B.getInstance();
console.log(a1.whoami());
console.log(a2.whoami());
console.log(a3.whoami());
console.log(b1.whoami());
console.log(b2.whoami());
console.log(b3.whoami());
Note that B inherits whoami from A and that the constructor call counters are never incremented past 1.
Obviously with this approach you can make no guarantee the singleton property holds for each class unless only the static constructors are used to generate instances (since the constructors are still accessible). I think it's a good compromise though.
In JavaScript, a singleton is just an object literal.
const a = {
options: {
i_am_a: "aaaaaa"
}
};
const b = {
options: {
i_am_b: "bbbbbb"
}
};
If you really need a constructor function, you can just write a function that returns an object.
function makeSingleton(options) {
return {
options
}
}
const a = makeSingleton({i_am_a: "aaaaaa"});
const b = makeSingleton({i_am_b: "bbbbbb"});
There's no inheritance chain here, just two object literals. If you absolutely need a class, you can just create one, but it's an unnecessary waste of resources and typing.
class Singleton {
constructor(options) {
this.options = options;
}
}
const a = new Singleton({i_am_a: "aaaaaa"});
const b = new Singleton({i_am_b: "bbbbbb"});
In terms of inheriting, if that's something you really need, you can use Object.create() or Object.assign(), depending on your needs. Be aware that both are shallow - they only work a single layer deep so modifying the child's options property would modify the parent's options property as well.
const a = {
options: {
i_am_a: "aaaaaa"
},
getOptions() {
return this.options;
}
};
const b = Object.create(a);
b.options.i_am_b: "bbbbbb";
a.options.i_am_b; // -> "bbbbbb"
b.getOptions(); // -> { i_am_a: "aaaaaa", i_am_b: "bbbbbb" }
Of course, you could use Object.create() or Object.assign() on the options as well.
To be honest, I think you either need a couple of instances of the same class, or a simple object literal without any inheritance.
const instances = {}
class Singleton {
constructor() {
const instance = instances[this.constructor];
if (instance == null) {
return instances[this.constructor] = this;
}
return instance;
}
}
class Foo extends Singleton {
constructor() {
super();
this.foo = "foo";
}
}
class Bar extends Singleton {
constructor() {
super();
this.foo = "bar";
}
}
const foo1 = new Foo();
const foo2 = new Foo();
const bar1 = new Bar();
const bar2 = new Bar();
console.log(foo1 === foo2, bar1 === bar2, foo1 === bar1, foo1.foo = 123, foo2, bar1);
well i don't know if it the best solution but what i did is to check if the constructor name is different then the class name. if so then i let it create a new instance because that mean i try to extend the class
here is a working example of my test
let instanceA = null;
let instanceB = null;
class A {
constructor(options) {
this.options = options;
if (instanceA === null) {
instanceA = this;
}
if(this.constructor.name !== "A"){
return this;
}
return instanceA;
}
method1(){
console.log(this.constructor.name)
}
}
class B extends A {
constructor(options) {
if (instanceB === null) {
super(options)
instanceB = this;
}
return instanceB;
}
}
const a = new A({
i_am_a: "aaaaaa"
});a
const b = new B({
i_am_b: "bbbbbb"
})
const c = new A({
i_am_c: "ccccc"
});
const d = new B({
i_am_d: "ddddd"
})
console.log(a.options)
console.log(b.options)
console.log(c.options)
console.log(d.options)
a.method1();
b.method1();
c.method1();
d.method1();

Capture `this` of caller inside class in JavaScript

Consider the following code:
class Abc {
funcAbc() {
console.log(this);
}
}
const abc = new Abc();
class Def {
constructor(func) {
this.func = func;
}
runFunc() {
this.func();
}
}
const def = new Def(abc.funcAbc);
def.runFunc();
I want this to be Abc when runFunc is called, but in the above implementation, this inside runFunc refers to Def. I understand this is happening because runFunc is a member of class Def. But is there any way I can capture 'original this', i.e. point this to Abc inside runFunc?
I cannot do const def = new Def(abc.funcAbc.bind(abc) because the consumer of class should not be bothered about setting the this context.
Note: This is not a theoretical question, it is an actual requirement in the project I am working on. The wrapper class takes in config, a part of which is a function. This function can also be a method on a class instance, using this inside it. Hence I need to preserve original this when calling the function from inside the wrapper class Def.
You are looking for bind() (or one of its variants).
class A {
constructor() {
this.hello = "hello";
}
run() {
console.log(this.hello);
}
}
class B {
constructor(func) {
this.func = func;
}
run() {
this.func();
}
}
const a = new A();
const b = new B(a.run.bind(a));
b.run();
When you bind() a function, it locks what this will be inside the function when the function is run, regardless of how it is invoked.
You could also wrap up the function in a closure:
class A {
constructor() {
this.hello = "hello";
this.run = ((self) => () => console.log(self.hello))(this);
}
}
class B {
constructor(func) {
this.func = func;
}
run() {
this.func();
}
}
const a = new A();
const b = new B(a.run);
b.run();
Or you could use the arrow function syntax. Note, this one requires the Babel class properties transform plugin to work:
class A {
constructor() {
this.hello = "hello";
}
run = () => {
console.log(this.hello);
}
}
class B {
constructor(func) {
this.func = func;
}
run() {
this.func();
}
}
const a = new A();
const b = new B(a.run);
b.run();
Using function.call(thisArg, args...) can set this to what ever you want. i.e., you could do:
class Abc {
func() {
console.log(this);
}
}
const abc = new Abc();
class Def {
constructor(source) {
this.func = source.func;
this.source = source;
}
runFunc() {
this.func.call(this.source);
}
}
const def = new Def(abc);
def.runFunc();
This solution assumes there will be a method on abc that is called func.

Is there a way to iterate over public methods inside a function scope?

Consider this code:
var Foo = function () {
this.bar = [];
this.hello = function () {
this.name = "world";
};
};
for (var property in Foo) {
alert(111);
}
It does nothing. Is there a way I can iterate over properties and public methods of Foo? It would work if Foo was object literal, like this:
var Foo = {
bar: [],
hello: function () {
this.name = "world";
}
};
for (var property in Foo) {
alert(111);
}
But I would prefer for it to be a function instead.
The reason I want to do this, I want to extend from Foo using mixin pattern.
http://jsfiddle.net/ChU2V/
You need an actual instance of Foo for this to work:
var foo = new Foo();
for (var property in foo) {
alert(111);
}
Otherwise, the properties are just "virtual" in the sense, that it's never reached program code.
Other than that, you can define the properties on the prototype:
var Foo = function() {};
Foo.prototype = {
bar: [],
hello: function () {
this.name = "world";
}
};
and then loop over Foo.prototype.
Finally, being a dynamic language, JS also allows you to go completely crazy, if you must:
var possible_props = Foo.toString().match(/\bthis\.\([a-zA-Z0-9_]+)\s*=/g);
// will yield an array similar to this:
// ["this.bar =", "this.hello ="]
Note however, that this is very error-prone and not recommended. For example, it doesn't catch cases like this:
var that = this;
that.baz = null;
for (var property in new Foo()) {
console.log(property);
}
Try
var Foo = function () {
this.bar = [];
this.hello = function () {
this.name = "world";
};
};
for (var property in new Foo() ) {
alert(111);
}
Notice the new Foo().
Fiddle updated.
http://jsfiddle.net/sujesharukil/ChU2V/2/
var fooInstance = new Foo();
for(var property in fooInstance){}
You have to create an instance of Foo in order to get the properties off of it.

Categories

Resources