I'm watching this lecture: http://www.youtube.com/watch?v=Kq4FpMe6cRs
// the speaker states that "'bar' is just some function
// that invokes whatever function is passed to it"
function bar(fn) {
fn();
}
function foo() {
var x = 8;
bar(function baz() { return x; });
}
Object.prototype.x = 'foo';
At minute 35, the above shown issue is presented. The lecturer states that some browsers will return foo
instead of 8
.
Why?
Btw, while writing this question, I figured it out, but I'll post this question anyway because it's an interesting issue. :)
Live demo: http://jsfiddle.net/simevidas/mHyKc/
Opera 11 alerts 'foo'
, all my other browsers (including IE9) return 8
.
Update: I take back what I said about having figured this out. It has something to do with the nested function being a named function. If you remove the name (baz
), then Opera returns 8
, which means that the issue only occurs with named nested functions.
But why?
I admit that I looked it up. I don't think I would have figured out this one that easily, to say the least. http://kangax.github.com/nfe/#spidermonkey-peculiarity
To understand the problem, the semantics of named function expressions is necessary. To quote the link, "the identifier of a named function expression is only available to the local scope of a function." Concretely that implies:
var fn = function aNamedFunction() {
typeof aNamedFunction; // "function"
};
typeof aNamedFunction; // "undefined"
To implement this behavior, SpiderMonkey and other JS engines create a dummy environment frame between the parent frame (i.e., the scope in which the function is defined) and the function's inner frame, which is created each time the function is invoked. This dummy frame is constructed as if new Object()
were invoked, and contains a mapping from aNamedFunction
to the function object itself.
In the question's code sample, x
is resolved by first looking in the function invocation's innermost frame. Since the function body does not declare var x;
, it is not found and the interpreter checks the frame one level up, which is the dummy frame. Since the dummy frame was created by new Object()
(or something semantically equivalent), looking up x
in the dummy frame will traverse the prototype chain, as it does for any other JavaScript object. Thus, it searches Object.prototype
, finds the string 'foo'
bound to x
, and returns it.
Thank goodness for ES5's consistent lexical scoping.
Alternative reading: http://dmitrysoshnikov.com/ecmascript/chapter-5-functions/#nfe-and-spidermonkey
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With