3

I am running a self executing function in javascript to not interfere with any global variables on a page. I have a string with the name of a function I want to call. The function is declared inside my self-executing function. Is there a way to call that function by using the string?

(function(document, window){
    var functionName = "myFunction";

    window[functionName](); //This does not work...        

    function myFunction(){
        //Do some stuff
    }

}(document, window)

I found this: How to execute a JavaScript function when I have its name as a string

but my function is self executing without a name, so I have no way to reference it with the window variable.

Community
  • 1
  • 1
Clint
  • 33
  • 1
  • 3

8 Answers8

4

This is just a cleaner version of what others have suggested, no global pollution, no eval, and no obscure variable references (arguments.callee and "this").

// Not sure why you were passing in doc and window, so I took it out for clarity
(function(){
    var funcs = {
      funcA: function () { return "a"},
      funcB: function () { return "b"}
    };

    var functionName = "funcA";
    funcs[functionName]();  
}();

This approach does require you to declare the functions before you use them.

Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
  • +1, for more protection by closure than mine. A bit verbose, but still. – theazureshadow Nov 05 '10 at 21:14
  • Passing in doc and window allows me to access those variables as local variables, then when I minify the script, 'document' and 'window' become 'a' and 'b' respectively, which shrinks my script that much more. Check out Paul Irish's post: http://paulirish.com/2010/10-things-i-learned-from-the-jquery-source/ – Clint Mar 25 '11 at 16:32
  • @Clint: How is this relevant to the answer I posted? In any case, the added benefit of the approach you mentioned, though maybe not that important, is the fact that accessing local variables is faster than global variables since you don't need to look up the scope chain. – Ruan Mendes Mar 25 '11 at 17:47
3

Without global pollution and avoids the non-strict arguments.callee method.

Based on this answer.

// This Immediately Invoked Function Expression is invoked via `call`.
// call() takes 1 or more arguments: the first being the scope you want the 
// invoked function be in; the other arguments become arguments of the function
// itself.
// In this case, the scope of our function is an empty object ({}).
// This object allows us to attach functions using the this.* syntax and call the 
// functions with this["string"]() without ever polluting the global namespace. 
// It is also forward compatible with ECMAScript 5's scrict-mode as it does not
// use arguments.callee.
(function(document, window){
    var functionName = "myFunction";

    // you'll need to define the function _before_ you call it.
    this.myFunction = function(){
        //Do some stuff
        alert('me');
    };


    this[functionName]();

}.call({}, document, window));
Community
  • 1
  • 1
David Murdoch
  • 87,823
  • 39
  • 148
  • 191
  • 1
    It's an interesting solution but kind of obscure. Basically you're creating a local variable that points to an empty object, but you're calling it "this". This confusing, I'm suggesting a similar less confusing approach – Ruan Mendes Nov 05 '10 at 20:16
  • Sorry, I like this better. I don't have to put my functions in an object before I use them, I can just assign them to the obscure "this" variable. Sweet. – Clint Dec 07 '10 at 23:15
  • No problem, it'll just take an extra 60 seconds for an intermediate JS developer to understand what is happening. – Ruan Mendes Jan 06 '11 at 00:10
  • @Juan Mendes. `//` Comments added explaining what is going on. – David Murdoch Jan 06 '11 at 13:24
1

You can use eval(functionName + "()") since any function of window is also a global function.

Mikhail
  • 8,692
  • 8
  • 56
  • 82
  • `eval(myFunction + "()")` would probably throw a `SyntaxError` exception (`myFunction` *is* the function object, not the name), I think you mean: `eval(functionName + "()");` – Christian C. Salvadó Nov 05 '10 at 20:15
1

Can you just store the myFunction function somewhere (such as on this a new object)?

See jsFiddle example:

(function (document, window){
    var functionName = "myFunction",
        myFunctions = {
            myFunction: function() { alert('myFunction'); }
        };

    myFunctions[functionName]();
}(document, window));
bdukes
  • 152,002
  • 23
  • 148
  • 175
  • Finally! An answer that doesn't devolve to a "just-use-eval" PHP approach! (Addition for poster: any object would work here to be able to elicit a value look-up -- including a new object (imagine `var fns = {myFunction: function () {...}}` in the execution context.) –  Nov 05 '10 at 19:37
  • 4
    Except that now the global namespace is polluted. this == window. – theazureshadow Nov 05 '10 at 19:42
  • @bdukes, what do you mean by PHP approach? – Ruan Mendes Nov 05 '10 at 20:27
  • 1
    Ah, now I like this a little better than my suggestion. Good call on the object literal. More consistently protected by closure. (The only downside is an additional local variable.) Oh, but Juan already posted this answer :) – theazureshadow Nov 05 '10 at 21:10
  • And you're creating a global myFunctions ;) – Ruan Mendes Nov 05 '10 at 21:20
  • 1
    @Juan no, that's a comma after the definition of `functionName`, not a semi-colon. I'm defining two local variables with that `var` statement. – bdukes Nov 05 '10 at 21:33
1

If you don't mind defining the function before you call it, you can use arguments.callee. This avoids eval.

(function (){
  arguments.callee.f = function() {
    console.log('test');
  };
  arguments.callee['f']();
})();
theazureshadow
  • 9,499
  • 5
  • 33
  • 48
  • This is much like David Murdoch, storing all the functions in an object, then calling it from the object. However, it suffers from the same problem, it's storing it in an obscure place, he stored in a "this" variable, you stored it in arguments.callee. See my solution, it's the same thing, just stores it in a local, clear, variable. – Ruan Mendes Nov 05 '10 at 20:26
  • Yet he wanted to call it as a string. Just trying to comply :) I agree that a local variable is generally better, but why do you say `arguments.callee` is "obscure"? It's perfectly well-defined, and protected by the closure just the same (since the function has no other reference). – theazureshadow Nov 05 '10 at 21:05
  • 2
    Because var functionList is more descriptive than arguments.callee, you're hijacking that object, just because it's available. – Ruan Mendes Nov 05 '10 at 21:22
0

You are correct. This is because your function is defined inside your self-executing function. Window has no knowledge of the inner closure. The best you could do is probably eval:

a la:

eval(functionName + "();");

, and it's probably not all that evil in this case: When is JavaScript's eval() not evil?

Community
  • 1
  • 1
Gopherkhan
  • 4,317
  • 4
  • 32
  • 54
0

Try: functionName.apply(context, arguments) or functionName.call(context[, argument 1, argument 2, ...]) (view reference)

Or (as a poster below mentioned) you can add the function to an object in the window namespace:

var newObj = {}; newObj.myFunction = function() { // stuff }

newObj.myFunction(); //calls the new function

Anthony Corbelli
  • 877
  • 4
  • 10
0

I guess I can't comment on answers, so I have to post it this way. Eval is too dangerous for me, since the actual function name is given by the user in a string.

  • Edit the question instead (or if that requires reputation too, comment on the question). –  Nov 05 '10 at 19:34
  • if the user is hand-specifying a function which you cannot predict what it may be - there's no way around it. If it's a choice of a function then you can have a `switch` to filter out only allowed functions. Lastly - nothing via javascript is really "dangerous" since it's client side. – Mikhail Nov 05 '10 at 19:37
  • Well, not really. According to the code above, the var containign the function name string is declared inside the anonymous function...it's not passed in. – Gopherkhan Nov 05 '10 at 19:38
  • That is just an example of what I am doing, not the real code. It can be dangerous because the actual script calls ajax posts to php files, so it could potentially cause some damage. – Clint Berry Nov 05 '10 at 19:41