2

There is something I don't understand about how eval works. Suppose I have a function foo:

function foo() {
    console.log("test");
}

And then I write

eval("foo()");

or

eval("foo" + "();");

The function foo is executed and I have "test" printed out. However, if I write:

eval("function foo() { console.log(\"foo\"); }();");

or

eval("function foo() { console.log(\"foo\"); }" + "();");

I get "SyntaxError: Unexpected token )". Why is that? I saw that when I pass the function name it is evaluated into the function's code, so I though it's supposed to be the same as "eval("foo" + "();");"

I'm using Chrome 27 if it makes any difference.

3
  • 2
    eval is meant to evaluate expressions, the last example isn't a single expression. Add an operator to the function declaration and you're there. That said eval-ing code like this is a terrible idea, so I'd suggest not to even begin to try and understand how it works Commented Jul 3, 2013 at 12:01
  • actually, eval is totally irrelevant here - that code will fail just in the same way without it. Commented Jul 3, 2013 at 12:38
  • @thg435: Yes, it will. In my answer I went into more detail, and explained why his last eval call actually contained a syntactically invalid statement Commented Jul 3, 2013 at 12:47

6 Answers 6

5

Because no answer is specific about why the last snippet fails, and nobody seems to be warning you for the dangers of eval:

eval, according to MDN:

A string representing a JavaScript expression, statement, or sequence of statements. The expression can include variables and properties of existing objects.

Your string, "function foo(){console.log('foo');}()" actually consists of 2 statements, that make no sense to the JS engine. Well, the second one doesn't:

function foo(){console.log('foo');}//function declaration, fine
;//<-- js adds implied statement terminator
()//no invocation, because no function reference, group nothing ==> does not compute, raise error

That's why you have to turn your function declaration statement into an expression, by adding an operator. Typically, this is the grouping operator: ()

(function foo(){ console.log('foo')});

The function declaration is now an expression, everything (the grouping () included) can be seen as a statement, unless the code that follows belongs to the code above. In this case, the invoking parentheses clearly do, so the JS engine sorts it out for you.
For clarity, some say the preferred notation is:

(function f(){}());//<-- invoking parentheses inside group

Which makes sense, because the invocation is part of the statement, after all.

If you don't like all these parentheses, any operator will do:

~function(){}();//bitwise not
+function(){}();//coerce to number

Are all equally valid. Note that they will change the possible return values of the function expression.

(function(){}());//resolves to undefined
+function(){}();//resolves to NaN, because undefined is NotANumber

How your eval could look, then, is any of the following:

eval("(function (){console.log('foo');}());");
eval("~function (){console.log('foo');}();");
eval("!function (){console.log('foo');}();");

And so on, and so forth...

Lastly, eval evaluates code in the global scope, so any code eval-ed containing functions can, and likely will, polute the global namespace. Mallicious code will also have access to everything, so be weary of XSS attacks and all other JS based techniques.
Bottom line, eval is evil, especially since all browsers now support JSON.parse natively, and for those that don't, there still is a tried and tested JSON2.js file out there.
Using eval in strict mode does make things slightly safer, but doesn't prevent XSS attacks at all, the code can still manipulate the DOM, for example, and reassign exposed DOM references or objects.

Google "why eval is evil" if you want to find out more.
Also check the ECMAScript specs

Sign up to request clarification or add additional context in comments.

2 Comments

()//invoke what? does not compute, raise error I actually think that this is interpreted as the grouping operator as well, but it cannot be empty. The syntax error is SyntaxError: Unexpected token ). If you replace () with (1) it works fine. Anyways, +1 for a thorough answer :)
@FelixKling, You're absolutely right... Because of what the OP was trying, I only looked at those parentheses as the way to invoke a function, will edit
1

function x() {} is a statement, whereas everything else you're doing is an expression, and you can only evaluate expressions.

The other answers explain how you can turn this statement into an expression. Thought I'd let you know why you actually get an error, though. :)

3 Comments

"you can only evaluate expressions" No, that's not true. eval("function foo() { console.log(\"foo\"); }"); or eval('if (true){alert(1);}') is perfectly fine. The problem is that function foo(){...}() is just invalid JavaScript.
You're right, although I still think trying to evaluate a statement is silly, but I guess js doesn't have a big distinction between statements and expressions like some languages do.
They are two separate chapters in the specification (11 and 12) though I cannot say how their distinction compares to other languages.
1
eval("(function() { console.log(\"foo\"); })()");

This is called a self-executing anonymous function.

4 Comments

foo in function foo() is not necessary ;-)
Right, since it's anonymous. I just copied the above and put brackets around it. (edited above)
Your answer is rather cryptic -- did you modify the code or copy it verbatim? How could I know without checking every single character? It looks like you just copied the code and says "this is what we call this" without explaining anything at all.
@guy777: It can be: (function foo(){ if (something === true){ something = false; setTimeout(foo, 1000);} alert('I was called with a timeout');}()). Instead of using arguments.callee, which is deprecated, a named function expression can be used, which doesn't pollute the global namespace
0

you would need to call

eval("function foo() { console.log(\"foo\"); };" + " foo();");

in order for this to work;

1 Comment

I think your answer would be more useful if you explained why, rather than just adding a character somewhere in the code. Can you explain how your code fixed the problem?
0

Put brackets around your function

eval("(function foo() { console.log(\"foo\"); })()");

It is like calling

(function foo() { console.log("foo"); })()

Comments

-2

The main thing you should understand about eval is that it is evil. Don't use it... ever. There is always a way to achieve the same thing by dynamically creating a script document or by using a closure.

Read Douglas Crockford: JavaScript the good parts.

5 Comments

eval has to be handled with care, yes, but that was not the question.
The question had already been answered. I was just sticking my two pence worth in. Didn't expect the Q&A police to arrest me for it. :-)
eval isn't evil. It's a tool, and it has its uses. It is true that when used incorrectly it can cause all kinds of problems, but that doesn't make it evil.
Well, since it doesn't answer the question (and you didn't even intend to), maybe you should have written a comment instead.
evaluated code is not compiled, so it will inevitably slow down execution. If placed in a loop, the effect is exponential. As I said, there are better ways to execute dynamic code. If you are not writing production quality code, then it is absolutely fine. However, prototyping in the real world doesn't actually exist. Inevitably, what you prototype will become production implementation. Scripting languages were invented for RAD, otherwise we would still be stuck with compilation and strict types.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.