0

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.

Nick
  • 2,261
  • 5
  • 33
  • 65
  • 1
    Why would you _want_ to do this? Wouldn't it be easier to just do `colorFunction(["red", true])`? The syntax for your custom "function" (I put the word function in quotes because this is really just a glorified function wrapper which doesn't do anything) will always be longer and more annoying track down in the code if issues arise than just using the function directly – GrumpyCrouton Jul 30 '19 at 12:36
  • 3
    Explain the problem you are trying to solve first in order that others can understand context better – charlietfl Jul 30 '19 at 12:39
  • @GrumpyCrouton I have a react component with a click event that fires off to 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. – Nick Jul 30 '19 at 12:42
  • @charlietfl I have a react component with a click event that fires off to 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. – Nick Jul 30 '19 at 12:42
  • 1
    Possible duplicate of [How do I call a dynamically-named method in Javascript?](https://stackoverflow.com/questions/969743/how-do-i-call-a-dynamically-named-method-in-javascript) – GrumpyCrouton Jul 30 '19 at 12:44
  • How about [currying or composite patterns](https://medium.com/@pragyan88/writing-middleware-composition-and-currying-elegance-in-javascript-8b15c98a541b) – zer00ne Jul 30 '19 at 12:46
  • 2
    Please read this [The XY Problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) – Yury Tarabanko Jul 30 '19 at 12:51

2 Answers2

2

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

Morphyish
  • 3,932
  • 8
  • 26
  • I'm pretty sure you meant `apply`, not `bind`. And yes, they definitely should be made properties of some local object - global functions should be avoided. – Bergi Jul 30 '19 at 13:17
0

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.

VLAZ
  • 26,331
  • 9
  • 49
  • 67