2

I'm trying to dynamically create function when iterating over an array and I need the arguments in the array to be set according to the value of the current index.

For example:

var array = ['apple','orange','banana'];

I need to have these three functions:

function() { return 'apple' };
function() { return 'orange' };
function() { return 'banana' };

I tried to return a constructed function from an external one but the expression in it won't evaluate and I end up with three of these:

function() { return array[i] };

Is there a way to dynamically create such a function without using eval()?

BarakChamo
  • 3,321
  • 5
  • 30
  • 38

3 Answers3

4

You can create the functions like so:

var funcs = {};
for (var i=0;i<array.length;i++)
{
    funcs[array[i]] = (function(val)
    {
        return function()
        {
            return val;
        };
    }(array[i]));
}

which can be called like so:

funcs.apple();// returns "apple"

But also, depending on the value of some var:

var someVar = 'banana';
if (funcs.hasOwnProperty(someVar))
{
    funcs[someVar]();
}

If what you're after is a single (possibly global) function, that depending on, for example, the URI, you just have to write this:

var myFunc = (function()
{
    var retVal = location.mathname.match(/^\/([^\/]+)/)[1];
    return function()
    {
        return retVal;
    };
}());

Note that the function won't be hoisted, as it is an expression.
I've written a lot about IIFE's (Immediatly Invoked Function Expressions), how they work and why, so best check my answer here if you don't fully understand these code snippets. It's quite easy once you get the logic, and you'll soon find yourself writing code like this all the time... tey really are incredibly powerful things, closures are!

Community
  • 1
  • 1
Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149
1

This is what I would do:

function constant(value) {
    return function () {
        return value;
    };
}

var array = ["apple", "orange", "banana"];

var fruit = array.map(constant);

alert(fruit[0]()); // apple
alert(fruit[1]()); // orange
alert(fruit[2]()); // banana

Simple. See the demo: http://jsfiddle.net/tfS2F/

You can also use the initial array as a key as follows:

alert(fruit[array.indexOf("orange")]()); // orange

See the demo: http://jsfiddle.net/tfS2F/1/

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
0

This one will not work, i leave it to illustrate the infamous loop problem:

The best way to achieve this would be to create a context var before creating the function, hope this illustrates it http://jsfiddle.net/jkymq/

var array = ['apple','orange','banana'];
var methods = {}
for (pos = 0 ; pos < array.length; pos ++){
    var target = array[pos];
    var newMethod = function(){alert (target);}
    methods[target] = newMethod;
}
for (foo in methods){
    methods[foo]();
}
Edorka
  • 1,781
  • 12
  • 24
  • 2
    `target` suffers from the infamous loop problem here, you need a closure! – Elias Van Ootegem Jul 01 '13 at 08:49
  • the scope is the newMethod declaration. Would it fail? – Edorka Jul 01 '13 at 08:54
  • 1
    The problem is, that variable declarations as in `var target` are hoisted at the top of the function. You don't get a new variable each time the loop iterates. Since you have only one variable and the functions in `methods` all refer to that variable, they will all evaluate to the same value ;) – phant0m Jul 01 '13 at 08:57
  • yes but I have a new var target in each iteration, the jsfiddle is working properly. – Edorka Jul 01 '13 at 09:01
  • 2
    @Edorka: Your fiddle works, because you're using the `target` variable every time. Change `target` to `foo` when looping over your methods object, and your code doesn't work anymore. your functions merely _reference_ the global `target` val, so their behaviour changes whenever the value of `target` changes.... also try changing the `method[target]();` call to `methods.apple();` call, it won't alert `apple` three times... – Elias Van Ootegem Jul 01 '13 at 09:07
  • @Edorka: Also: you don't get a new `target` on each iteration: JS doesn't support block-scopes (just yet, it might be comming in ES6 - harmony) – Elias Van Ootegem Jul 01 '13 at 09:13