Exploring Javascript's eval() Capabilities And Closure Scoping
A little while ago, while working on the jQuery Template Markup Language (JTML) code, I used Javascript's Function() constructor to compile template markup and found out that it doesn't create a closure to the context in which it was executed. I blogged about this and was promptly told that according to the Javascript spec, Function() scopes to the window object and eval() scopes to the calling context. I often use eval() to evaluate JSON strings; but, I very rarely ever use it to evaluate Javascript source code.
I didn't think much more about this until last week when I attended the NYCJS (New York City Javascript) Meetup group. In the meeting, Elijah Insua showed off his JSDom project. And, in his demo code, he pulled in the entire jQuery library as a string and eval()'d it into effect. This really opened my eyes up to the power of the eval() function and I figured it was time to do a little digging of my own.
In the following demo, I have two Script tags. One is of type, "text/source-code", which will not be executed by the browser. The other is of type, "text/javascript", which will, of course, be our executing Javascript code. In the Javascript code, I am going to get the html (innerHTML) of the "source-code" tag and eval() it in its entirety. As I do this, I am going to see what kind of closures and context-chains the evaluated function creates.
<!DOCTYPE HTML>
<html>
<head>
<title>Javascript eval() - How Much Is Too Much?</title>
</head>
<body>
<h1>
Javascript eval() - How Much Is Too Much?
</h1>
<!---
Note that the TYPE of this Script tag is not Javascript,
but rather "source-code". We will be putting code in
here to see if it executes properly within an eval()
function call.
--->
<script id="source" type="text/source-code">
// Create a self-executing function. This is to ensure that
// nothing defined here goes into the global scope (helps to
// figure out what bindings are taking place).
(function(){
// Create a local value.
var description = "beautiful";
// Create a function. This function will reference both
// the local variable above as well as one that has
// not yet been bound to this context.
var whisperSweetNothings = function(){
// Alert a message that combines the local, private
// scope and some other scoped-variable (name).
console.log(
name +
", you are looking quite " +
description +
" today."
);
};
// Return function.
return( whisperSweetNothings );
})();
</script>
<!---
Now, let's actually run some Javascript where we evaluate
the source-code defined above.
--->
<script type="text/javascript">
// Create a self-executing function. This one is to ensure
// that any binding isn't simply going to the window scope.
(function(){
// Get the source code element.
var source = document.getElementById( "source" );
// Create a local variable for the name.
var name = "Erika";
// Evaluate the source code, which should return a
// function reference.
var whisperSweetNothings = eval( source.innerHTML );
// Try to execute this function.
//
// NOTE: This function uses reference defined in two
// different self-execution functions. This will help us
// understand how the closure is being created.
whisperSweetNothings();
})();
</script>
</body>
</html>
As you can see, both Script tags start off by defining self-executing functions. I did this to make sure that nothing was accidentally scoping to the Window object without my understanding. Within the source-code's self-executing function, I define another function, whisperSweetNothings(). This whisperSweetNothings() function creates a closure with its context, which happens to house the variable, "description." This source code is then, itself, eval()'d within the primary code's self-execution function, which happens to house another variable, "name." The whisperSweetNothings() function references both the "description" and "name" variables, which will only work if has both contexts in its closure chain.
When we run the above code, we get the following console output:
Erika, you are looking quite beautiful today.
As you can see, the whisperSweetNothings() function properly referenced the description variable (beautiful) in its defining context as well as the name variable (Erika) in its calling context. Had I done the same thing with the Function() constructor, the Name variable would have errored-out as undefined.
This is some pretty cool stuff! Not only is eval() quite capable of compiling complex Javascript code on the fly, the fact that it compiles in the context of its calling scope seems very powerful. I'll have to do some more experimentation to see if this should be used in conjunction with the Function() constructor or, if it should be used in lieu of the Function() constructor... and when to know the difference.
Want to use code from this post? Check out the license.
Reader Comments
Everytime I've seen eval, it's been followed by 'thats a security threat'. I suppose the idea is that code could be injected into the string prior to eval then executed as if it was running on your page. This would create a possible XSS attack.
Do you or anyone else have any concerns over security using eval?
@Drew,
The only way I can see eval() being a concern is if you are eval()'ing data that comes back over an HTTP request (ala AJAX). If you are eval() code that you wrote, then it should be of no concern. If someone is able to intercept *that* kind of call, then they already way behind enemy lines and probably can execute any kind of code they want.
As far as the HTTP-delivered JSON data that gets eval()'d, yeah, I suppose if someone intercepts your request and messes with your JSON data, it could be malicious.
hi Ben,
I'm a naive developer. I just want to say that this post is "super cool".
@Zero,
Thanks my man. I am glad you liked it!
Really nice blog with easy to understand instructions.
Thanks for spending time to write this blog entry.
@Pratik,
My pleasure - just happy you are finding value in it.
Cool Article, Thank you Ben
I would like to understand this better but when I run the code in a html page the only thing that displays is header text. Javascript eval() - How Much Is Too Much? . I tried it in IE and Chrome. please advise how i can get it to work thanks..
@Jonah,
It's probably the "console.log()" call that is tripping you up. That is part of the FireBug Firefox plugin. If you want this to run in another browser, you have to remove that call (perhaps replace with an alert() call).
Hlo ben,
what if i use
var text = eval("TeamName_" + teamCount);
*team count can b any integer value.
what are the consequences of this. Can you pls tell me . i am new in javascript. and please tell me how eval works here.
Thanks.
@Mukul,
In this case, the string will concatenate to something like TeamName_7... then, the eval() method will evaluate that value as if it were a variable name, returning said variable's value into the new variable, "text."
Thanks ben,
Its really very helpful.First time i understand the use of eval in this scenario.
Thanks for giving time for my problem and for your support too.
One more question ben.In my problem we can simply use
var text = "TeamName_" + teamCount;
then why use
var text = eval("TeamName_" + teamCount);
Actually i want to know the difference between the two. Is their any advantage if using eval?
some peoople says eval is very harmful.it reloads the whole new javascript environement..then y use eval?
sorry for so many questions in one..hope you will not get irritate.
@Mukul,
No problem at all - eval() executes a string as a chunk of Javascript code - it's basically like you're using Javascript to write code that is then executed as Javascript.
To answer your comparison question, imagine I had this code:
var girl1 = "Jill";
var girl2 = "Kim";
var data1 = ("girl" + 1);
var data2 = eval("girl" + 2);
... when I run this, my data variables will contain the following values:
data1 ==> "girl1"
data2 ==> "Kim"
As you can see, when you use the plus operator (+) to perform string concatenation in the first data assignment, all you get is the result string. However, in the second assignment, when you wrap the string concatenation in eval(), not only do you get the string concatenation (girl + 2), you then further evaluate that string *as if it were Javascript code*, which gives us the value contained within the variable, "girl2".
Does that help at all? You only need to use eval() when you want to dynamically evaluate some code.
Thank you so much Ben. I got my answer. Really very helpful.I was searching it for so long..
But Ben as i saw its very useful,can i use eval() frequently?
@Mukul,
You can use eval() when ever you want; but, I don't think you will have to use it that often. Typically, you can find better ways to reference variables.
"eval()" has capabilities not otherwise realizable.
It permits dynamic generation of JavaScript sequences by the server.
It has the disadvantage that it is slow.
Very interesting post! You encoruraged me to use eval wich i had in mind to implement a sort of templating of js code in wich i can use to pass by reference context variables to pseudo functions (blocks)! Very powerfull! Eval is not evil!
I am trying to use eval to bring the entire jQuery library into global scope. I have escaped all quotes and backslashes although I keep getting an error: Can't convert an undefined object. I also removed all comments and newlines. I ran it through jslint and there were no errors.
Does anyone have an idea as to why I get the undefined variable?
I really enjoyed this post, though the security issue is very real, for some reason, only the most malicious hackers seem to know this. I had some sites defaced through this function 2 months ago. (disclaim) I'll do my best to explain without inadvertently posting a "how to destroy the web with eval" guide. Please delete this if you feel it's instruction negatively effects security. (/disclaim)
Currently, web content is expected to be manipulated after rendering in the browser, using eval on the client side opens up the browser and the server to code injection attack as it is available for manipulation by any script delivered in the same session. You should not use eval in front end, client side, JavaScript. A site, posing as a portal, could instruct a browser to render your page as part of their page, and once rendered in the browser, manipulate the code inside your function that contains an eval() call, and simply call the function, executing their code in the context of your server of origin. In a best case scenario, the hacked browser just fraudulently maps visitor clicks to ad links that render transparently to your visitors. In a worst case scenario, it injects server side calls instructing the server serving your website to append a script to your site index that contains an equally malificent call to the eval function of your server side scripting environment, such as php eval() , turning every visit to your site into another execution opportunity on the server as well as the visitors browser. As the calling scope is the owner of the most privileged context, you can't use it on the front end without opening your server session and the visitors browser session to foreign code execution.
There are a couple things I left out of the attack scenario intentionally, to avoid spreading bad practices, I hope I conveyed the risk, it's a security issue that unfortunately can't easily be patched in the stack, developers need to understand that eval's utility lies in server side and non public facing applications, and open to the anonymous public is a time bomb at best.
I don't like how some developers say things like 'NEVER use eval'. There is a logic behind this train of through, but I think it's exaggerated; there are certain specific (albeit unusual) problems for which eval is an ideal solution. The trick is check/escape all user input before you run it to mitigate the risk of code injection; just as you would do with an SQL query.
Thank a ton. That was a valuable information.
ben you made this look extremely simple .Thanks for your time
<!DOCTYPE HTML>
<html>
<head>
<title>Javascript eval() - How Much Is Too Much?</title>
</head>
<body>
<script id="source" type="text/source-code">
(function(){
var description = "beautiful"
return function(){
alert(
name +
", you are looking quite " +
description +
" today."
)
}
})()
</script>
<script type="text/javascript">
(function(){
var name = "Erika";
eval( document.all.source.innerHTML )();
})();
</script>
</body>
</html>