Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
442 views
in Technique[技术] by (71.8m points)

javascript - Using eval to execute functions

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...