0

Since eval is no longer used for several reasons, I'm dealing with some problems calling functions. So I'm making a "Function Caller" to replace eval. First of all: am I bringing back eval issuses like security, speed and debugging difficulties? Here is the testing code:

function globalFunc(p){
    console.log("Run globalFunc: "+p);
}
var globalVarFunc = function(p){
    console.log("Run globalVarFunc: "+p);
}
function ini(){
    var varFunc = function(p){
        console.log("Run varFunc: "+p);
    }
    //
    funcCaller(function(p){ console.log("Run anonymous function: "+p); }, "anonymous");
    funcCaller('function(p){ console.log("Run anonymous function: "+p); }', "anonymous");
    funcCaller(globalFunc, "global function");
    funcCaller("globalFunc", "global string");
    funcCaller(globalVarFunc, "globalVarFunc function");
    funcCaller("globalVarFunc", "globalVarFunc string");
    funcCaller(localFunc, "local function");
    funcCaller("localFunc", "local string");
    funcCaller(varFunc, "local var function");
    funcCaller("varFunc", "local var string");
    //
    function localFunc(p){
        console.log("Run localFunc: "+p);
    }
}
function funcCaller(func, p){
    if(typeof(func)=="string"){
        p += " as string";
        try { window[func](p); } catch(e) { 
            console.log("Catch window error trying "+p) ;
            try { this[func](p); } catch(e) { console.log("Catch this error trying "+p) }
        }
    } else if(typeof(func)=="function"){
        p += " as function";
        try { func(p); } catch(e) { console.log("Catch error trying "+p) }
    } else {
        console.log("Unknown situation: "+typeof(func)+" trying "+p);
    }
}

The log result by Safari 5.1:

Run anonymous function: anonymous as function
Catch window error trying anonymous as string
Catch this error trying anonymous as string
Run globalFunc: global function as function
Run globalFunc: global string as string
Run globalVarFunc: globalVarFunc function as function
Run globalVarFunc: globalVarFunc string as string
Run localFunc: local function as function
Catch window error trying local string as string
Catch this error trying local string as string
Run varFunc: local var function as function
Catch window error trying local var string as string
Catch this error trying local var string as string

Maybe to run an anonymous function as string is not advisable, same problem as eval. But why "varFunc" doesn't work if "globalVarFunc" does?

EDIT: I wish at list to run "varFunc".

Gustavo
  • 1,673
  • 4
  • 24
  • 39
  • 2
    What exactly is it that you're trying to achieve? – Pointy Aug 09 '14 at 14:23
  • Also, "varFunc" **did** work - it's right there in your debug output! – Pointy Aug 09 '14 at 14:24
  • @Pointy - I think it's the literal `"varFunc"` the OP is asking about. In other words, the OP doesn't understand passing references and scope. – Jared Farrish Aug 09 '14 at 14:28
  • If you [really want & need `eval`](http://stackoverflow.com/q/197769/1048572), then you should use plain `eval`, instead of some crude workaround that tries to emulate it. However, there is almost always a better solution. – Bergi Aug 09 '14 at 14:32
  • I wrote the main goal of the question above. @JaredFarish: you're right! Bergi: I think eval will be deprecated, will stop working at all. – Gustavo Aug 09 '14 at 14:55

1 Answers1

1

What I think you're missing are the relationship between two concepts:

  • Pass by reference
  • Scope

Let's take a simplified example:

function check(arg) {
    try {
        var func = new Function('return ' + arg + '()');

        console.log(typeof arg, this);
        console.log(func);
        console.log(func());
    } catch (e) {
        console.log(e);
    }
}

function run() {
    function test() {
        console.log('Local test() run.');
    }

    check(test);
    check('test');
}

var r = new run();

http://jsfiddle.net/userdude/7Lz15qou/

When run() is called, within it has it's own scope, which is local to itself. The variables and function declared within it have their own references that are only seen within function run(){}.

When you pass run(test), you're passing the variable's value by looking up it's reference, which allows it to be understood as a copy; as you can see here, you don't affect the originally-referenced variable, you change it's local instance*.

The reason why this is important is that, without the value associated with that reference in the new scope (a function value in your case), all you have is a string with no relation to it's original value. eval() or not, you can't call something which is simply undefined in that scope; it's illogical.

It's a bit like having two show rings and a magical cage with lions in one ring. When you send a Lion to the other ring, ring(Lion), you've pointed at the cage and said get a copy of the lion and put it in that ring.

When you say ring('Lion'), you've told the other ring Lion, but there is no Lion defined. So that ring is running around saying Lion! Lion! but there's no Lion, only a word. Lion is undefined, so instead of getting a show, you get a comical, yet faulty result. People will probably want their money back.

This is why what you're trying to do won't work.


* Many languages allow you to pass by reference, but Javascript does not.

Jared Farrish
  • 48,585
  • 17
  • 95
  • 104
  • jsfiddle is down, I'll do more tests here, I wish to understand this concepts once for all! – Gustavo Aug 09 '14 at 15:39
  • While jsfiddle don't come back, I tested your mind blowing code. I understood that, in my code, globalFunc is already defined inside funcCaller because both are in the root. The lion jump inside the cage, and confused my mind. Going further, I think might be a difference between passing a string and a function to the caller: will be running outside or inside the cage? This might be outside the scope of the question, your call. The answer seams to be that sometimes a string won't work and is not a problem of code, but of scope. Thanks so far! – Gustavo Aug 09 '14 at 19:10
  • The answer is that a string has to reference something that *evaluates* as pointing to a function (the *cage* represents the box storing the function in memory, the reference is it's address) for it to call a function. In reality, the *cage* is copied with a lion *in it*. When you call a *global* function, you're really calling an implicit `window['functionName']()` (or `window.functionName()`). In other words, you're referencing an object property pointing to a function in memory that evaluates to that string. For another take, see: http://jsfiddle.net/userdude/7Lz15qou/2/ – Jared Farrish Aug 09 '14 at 20:21