Nick
Nick

Reputation: 2551

Dynamically build a function

Is it possible to dynamically build a function based off another functions parameters?

For example:

Base Function:

const someFunction = (functionName, functionParams) => {
  // Function Built here
};

someFunction("colorFunction", ["red", true]);

I'd like it to build something similar to this: I'd need to deconstruct the Array into individual params, but I'm not sure how simple that is? And I have no idea how I'd use the first String to call the function name?

functionName(...functionParams);

Which in my head would sort of work like this:

const colorFunction = (color, bool) => {
  console.log("Colour: " + color);
  console.log("Bool: " + bool);
};

Bit confused by this - I feel like I'm not a million miles away, but I'm not certain! Any help would be great, thanks!!

Edit - Why?

I have a react component with a click event that fires off a redux action. Ideally this action would fire some stuff over to my reducer, and asynchronously call this "dynamic" function. I can do this with a load of if/elses, but I don't think that's a very clean way of achieving this, if building a function this way is possible.

Upvotes: 0

Views: 121

Answers (2)

Morphyish
Morphyish

Reputation: 4062

First you need to determine where the functions you want to call dynamically are stored. If they are global functions then you can call them using window:

const someFunction = (functionName, functionParams) => {
    window[functionName]();
};

If they are methods of an object, then you can do something similar using the object:

const someFunction = (functionName, functionParams) => {
    myObject[functionName]();
};

As for how to pass the arguments, you have a couple options here. If you are running a recent version of JS, or using polyfills, then you can indeed use the spread operator:

const someFunction = (functionName, functionParams) => {
    window[functionName](...functionParams);
};

Otherwise you can always rely on the apply method:

const someFunction = (functionName, functionParams) => {
    window[functionName].apply(null, functionParams);
};

The first argument in the apply method is the context you wish to pass to your function, in your case it doesn't seem necessary, hence the null value.

Edit: corrected bind with apply as mentionned by Bergi

Upvotes: 2

VLAZ
VLAZ

Reputation: 28983

What you are looking for is Function#bind in order to make a thunk - basically a function that takes no parameters and it's used for delayed computation.

Using .bind you can do a partial application on a function. In your case, you would just apply all arguments and only leave the execution step at the end:

//your function
const colorFunction = (color, bool) => {
  console.log("Colour: " + color);
  console.log("Bool: " + bool);
};

//partially apply all argument to it

const thunk = colorFunction.bind(null, "red", true);

//this can now be passed around and executed at a later point
setTimeout(thunk, 3000);

console.log("wait 3 seconds");

Since functions are first class members in JavaScript, you can pass any function this way. If you really need a function that turns others into thunks then you can very easily do that:

const toThunk = (func, args) => func.bind(null, ...args);

Which is your someFunction but with just the name and parameters re-named for a bit of clarity.

Upvotes: 0

Related Questions