Jecimi
Jecimi

Reputation: 4173

Pass specific method of a specific instance as parameter

I would like to have a class where I can specify "Instance a1 of A, you will call the function computeValue() of the specific instance b1 of B when you need the value". I need to specify which method of which instance will be used by this instance.

So far I found two solutions :

The eval() solution :

class A {

  constructor(funcToCall) {
    this.funcToCall = funcToCall;
  }

  displayValue() {
    console.log(eval(this.funcToCall));
  }
}

class B {

  constructor(value) {
    this.value = value;
  }

  computeValue() {
    return this.value;
  }
}


var b1 = new B(1);
var a1 = new A("b1.computeValue()");
a1.displayValue();// Displays "1", okay

The function.call() solution :

class A {

  constructor(funcToCall, instanceToCallItOn) {
    this.funcToCall = funcToCall;
    this.instanceToCallItOn = instanceToCallItOn;
  }

  displayValue() {
    console.log(this.funcToCall.call(this.instanceToCallItOn));
  }
}

class B {

  constructor(value) {
    this.value = value;
  }

  computeValue() {
    return this.value;
  }
}


var b1 = new B(1);
var a1 = new A(b1.computeValue, b1);
a1.displayValue(); // Displays "1", okay

So to me the eval() solution seems very dirty and the function.call() seems better.

Which solution is the best ? Is there a more elegant solution to this problem ?

Upvotes: 0

Views: 64

Answers (2)

Barmar
Barmar

Reputation: 781004

Using functions should almost always be preferred over eval(). You can simplify the second method by using .bind(), so you don't need to pass the function and instance separately.

class A {
    constructor(funcToCall) {
        this.funcToCall = funcToCall;
    }

    displayValue() {
        console.log(this.funcToCall());
    }
}

// class B is the same as yours

var b1 = new B(1);
var a1 = new A(b1.computeValue.bind(b1));
a1.displayValue();

Or if you still prefer to pass the instance separately, you can use .bind() in the constructor:

class A {
    constructor(funcToCall, instance) {
        this.funcToCall = funcToCall.bind(instance);
    }

    displayValue() {
        console.log(this.funcToCall());
    }
}

Upvotes: 1

Bergi
Bergi

Reputation: 664538

Yes, that eval thing is very dirty and won't even work if b1 is not in the proper scope. Using call is fine, but you can do better:

  • Pass a combination of instance and methodname:

    class A {
      constructor(instance, methodName) {
        this.instance = instancen;
        this.methodToCall = methodName;
      }
    
      displayValue() {
        console.log(this.instance[this.methodToCall]());
      }
    }
    var a1 = new A(new B(1), "computeValue);
    
  • Pass a function that does the method call and whatever else it needs on its own:

    class A {
      constructor(funcToCall) {
        this.funcToCall = funcToCall;
      }
    
      displayValue() {
        console.log(this.funcToCall());
      }
    }
    var b1 = new B(1);
    var a1 = new A(() => b1.computeValue());
    // or:   new A(b1.computeValue.bind(b1))
    // or anything else
    

Upvotes: 1

Related Questions