Execute methods on returned class without using variable

Viewed 72

I have function that is returning class instance. This function/class is external, so I cannot rewrite methods to return "this" to make it chainable. Is there a short way to execute methods in that instance without storing it in const/let when I do not need them later? It is not a problem, I just wanna know if this is possible, like executing some callback, anonymous function or whatever cloud it be called.

const demo1 = generate()
demo1.a()
demo1.b()
demo1.c()

const demo2 = generate()
demo2.a()
demo2.c()

I was thinking of something like this, which of course does not work:

generate()(demo) => {
  demo.a()
  demo.b()
  demo.c()
}

generate()(demo) => {
  demo.a()
  demo.c()
}
5 Answers

Proxy

function generate() {
  return new class {
    a(v) { console.log(`call ${v}`) }
    b(v) { console.log(`call ${v}`) }
    c(v) { console.log(`call ${v}`) }
  }
}

function proxyGenerate() {
  return new Proxy(generate(), {
    get(target, prop, receiver) {
      if (prop in target) {
        return (...arg) => (target[prop](...arg), receiver)
      } else {
        throw new Error('Error')
      }
    }
  })
}

const pInstance = proxyGenerate()
pInstance.a('1').b('2').c('3')
try {
  pInstance.error()
} catch (e) {
  console.log(e)
}

Disclaimer: don't use first example in production code, unless you 200% sure that you know what exactly you are doing and why you are doing it. For example, I did this "for fun" and in "educational purposes", and have no intention to use such code in production ever.

If you are curious about technical solution, you can use instance.constructor to extend your own class of, and then rewrite methods that you want to make chainable, like this:

class Internal {
  logA() {
    console.log("a");
  }
  logB() {
    console.log("b");
  }
  logC() {
    console.log("c");
  }
}

function getInstance() {
  return new Internal();
}

class Wrapper extends getInstance().constructor {
  logA() {
    super.logA();
    return this;
  }

  logB() {
    super.logB();
    return this;
  }

  logC() {
    super.logC();
    return this;
  }
}

let myOwnInstanceWithBLackJackAndLadies = new Wrapper();

myOwnInstanceWithBLackJackAndLadies.logA().logB().logC();

Now. From dirty hacks to actual life. In your second example you are trying to pass instance into function. With this you will actually save instance in temporary variable and it is doable with IIFE. It will not be chainable, but it will not pollute your scope with new variable for that instance:

class Internal {
  logA() {
    console.log("a");
  }
  logB() {
    console.log("b");
  }
  logC() {
    console.log("c");
  }
}

function getInstance() {
  return new Internal();
}

(function (instance) {
  instance.logA();
  instance.logB();
  instance.logC();
})(getInstance());

Well you could destructure the methods out of the class. Like so

class RecordName {
    constructor(name) {
        this.name = name;
    }

    get personName() {
        return this.name;
    }
    
    set personName(x) {
        this.name = x;
    }
}

let {personName} = new RecordName('a.mola');

console.log(personName); // a.mola

personName = 'Steve Jobs';

console.log(personName); // Steve Jobs

Here’s a solution similar to the IIFE solution, but which requires less code and keeps the code written in the same order it executes: Use small blocks containing block-scoped variables or constants.

{
    const demo = generate();
    demo.a();
    demo.b();
    demo.c();
}
{
    const demo = generate();
    demo.a();
    demo.c();
}

Unlike var declarations, which are function-scoped, let and const declarations are block-scoped. A block is most often used with an if statement, for loop, or while loop, but a block can also occur by itself.

First thing, is if its a class you need to use 'new' keyword.

Second thing is you can just do it like this

new generate().a()

Though if you are calling multiple methods it makes sense to store it in a variable

Related