Vojtěch
Vojtěch

Reputation: 12416

Getting rid of eval

I have a name of a method as a string in javascript variable and I would like to get a result of its call to variable:

var myMethod = "methodToBeCalled";
var result;
eval("result = "+myMethod+"();")

This works and there are no problems. But this code is inacceptable for Google Closure Compiler. How can I modify it to work with it? Thanks!

EDIT:

It seems the proposed solutions does not work when the name of the method is inside of some object, for instance:

var myFunction = function () { return "foo!" }
var myObject = {
    itsMethod: function() { return "foo!" }
};
...
var fstMethodToCall = "myFunction"
var sndMethodToCall = "myObject.itsMethod";
...
window[fstMethodToCall](); // foo!
window[sndMethodToCall](); // undefined

Upvotes: 0

Views: 610

Answers (3)

John
John

Reputation: 5468

The Closure Compiler doesn't prohibit 'eval', you can continue to use it if you find it convenient but you have to understand that the compiler doesn't try to understand what is going on in your eval statement and assumes your eval is "safe":

function f(x, y) {
  alert(eval("y")); // fails: hidden reference to "y"
  alert(eval('"'+x+'"'));   // might be valid
}
f('me', 'you');

When the compiler optimizes this function it tries to remove "y" and renamed the remain parameter. This will the first eval to fail as "y" no longer exists. The second eval would correct display the alert "me".

So with SIMPLE optimizations, you can use eval to reference global variables and object properties as these are not renamed or removed (but not local ones).

With ADVANCED optimizations, it is a little trickier, as the compiler tries to remove and rename global as well as local variables. So you need to export the values you need to have preserved. This is also true if you use a string to try to reference a name by other means:

var methodName = "myMethod";
(window[methodName])()

or

var methodName = "myMethod";
eval(methodName+"()")

the compiler simply doesn't try to determine if "methodName" is a reference to a function. Here is a simply example of an ADVANCED mode export:

window['myMethod'] = myMethod;

The assignment does two things: it preserves the myMethod function if it would otherwise be removed and it gives it a fixed name by assigning it to a property using a string. If you do need to reference local values, you need to be a little trickier and use a Function constructor. A definition of "f" from my first example, that can eval locals:

var f = new Function("x", "y", "alert(eval('y')); alert(eval('\"' + x + '\"'));");

You may find this page useful:

https://developers.google.com/closure/compiler/docs/limitations

Upvotes: 1

Matt
Matt

Reputation: 41832

Assuming you are not in a nested scope of some kind, try:

var result = window['methodToBeCalled']();

or

var myMethod = 'methodToBeCalled';
var result = window[myMethod]();

To execute an arbitrary function of arbitrary depth based on a string specification, while not executing eval:

var SomeObject = {
    level1: {
        level2: {
            someFunc: function () {
                console.log('hello');
            }
        }
    }
};

var target = 'SomeObject.level1.level2.someFunc';

var obj;
var split = target.split('.');
for (var i = 0; i < split.length; i++) {
    obj = (obj || window)[split[i]];
}

obj();

Upvotes: 2

SLaks
SLaks

Reputation: 887453

You can use indexer notation:

result = window[myMethod]();

Upvotes: 2

Related Questions