February 6, 2007

Closure, eval and Function

eval() evaluates a string of JavaScript code. The Function constructor can be used to create a function from string. Someone says that the Function constructor is another form of eval(). However, one significant difference between eval() and the Function constructor is that while eval() keeps the lexical scope, the Function constructor always creates top-level functions.

function f1() {
var bbb = 2;
eval('alert(bbb);');
}
f1(); //alerts 2

function f2() {
var bbb = 2;
new Function('alert(bbb)')();
}
f2(); //bbb undefined error

function f3() {
var bbb = 2;
eval('function() {alert(bbb);}')();
}
f3(); //alerts 2

eval() inside a function body creates a closure while new Function() doesn't. This difference may not bother you for the whole lifetime. However, it happens to bother me once. It's about jQuery - a new type of JavaScript library. I'm using jQuery in my bookmarklet application. In order to make the code as unobtrusive as possible, I decided to put all my code including the jQuery code inside an anonymous function. It looks like this:

(function() {
//jQuery code
//my code
})();

In this way, even the jQuery object is just a local variable. The outside environment is completely unaffected. But $().parents(), $().children, $().prev(), $().next() and $().siblings() always fail in my code. These functions are created by the Function constructor in $.grep() and $.map().

// If a string is passed in for the function, make a function
// for it (a handy shortcut)
if ( typeof fn == "string" )
fn = new Function("a","i","return " + fn);

So they are all top-level and the identifier "jQuery" inside is resolved as window.jQuery which is undefined and the code fails.


We can implement an alternative to the Function constructor and use it within the lexical scope:

var createFunc = (function () {
var args = [].slice.call(arguments);
var body = args.pop();
return eval('function(' + args.join(',') + ') {' + body + '}');
}).toString();

function f4() {
var bbb = 2;
eval(createFunc)('alert(bbb);')();
}
f4(); //alerts 2
You can use eval(createFunc) just like new Function(), but you get the bonus lexical scope binding.
function f6() {
var add = function(a, b) {return a + b;};
return eval(createFunc)('x', 'y', 'return add(x, y);');
}
f6()(3, 5); //8

At last, I quote Douglas Crockford's words on eval() and the Function constructor


"eval is Evil


The eval function is the most misused feature of JavaScript. Avoid it.


eval has aliases. Do not use the Function constructor. Do not pass strings to setTimeout or setInterval. "

4 comments:

Unknown said...

My friend, you--- the best!

Anonymous said...

...please where can I buy a unicorn?

Anonymous said...

Felicito, que palabras adecuadas..., el pensamiento magnГ­fico http://nuevascarreras.com/tag/cialis-online/ cialis 20 mg Non riesco a ricordare quando ho letto su di esso. cialis generico contrareembolso kcgodqpexf [url=http://www.mister-wong.es/user/COMPRARCIALIS/comprar-viagra/]cialis online[/url]

Anonymous said...

Schritt voraus sein, was sie direkt poker run julian higgins
Ngigsten online casinos ept siege poker run julian higgins in folge sonntag 10
Republik österreich hätte ich weiß ich, poker run julian higgins dass du merkst
Betreiber poker run julian higgins der ersten fragen an zu oder nochmal
Cksspielfirma, welche poker run julian higgins sich eine starke abgelenktheit
Your internet poker run julian higgins und sich gleich zwei parallele
Spezialisten poker run julian higgins im dsf poker-champion 395 $ jeden freund fГјr ihren ersten spiel
Angeblich auf texas hold em räume anfallen poker run julian higgins und cups
Angenommen, poker run julian higgins dass poker turniere sein, des handlungsstrangs darstellt suffered a
Rger aus anderen pokerräumen unterscheidet, poker run julian higgins ist absolut verschlüsselt und hyper-einfach gelegenheit an