JavaScript function definition inside a with() block: Firefox is the oddball
February 14th, 2009I came across yet another browser discrepancy with a rather obscure scenario: A function declaration within a with block:
var obj = { a: 1 };
with (obj) {
function foo() {
return typeof a;
}
}
alert(foo()); // alerts "number" on Firefox, "undefined" on others
This exposes the object properties to the function scope only on Firefox. On all other browsers that I’ve tested with (IE, Safari, Opera and Chrome), the property isn’t visible inside the function.
February 14th, 2009 at 7:10 am
I believe firefox treats all function definitions as equivalent to function expressions, try
with(obj) { var foo = function() { return typeof a; } } alert(foo())I suspect you’ll find that they all produce number in that case
March 24th, 2009 at 10:15 pm
@Olivier: You’re right, it works (returns “number”) on all browsers with the variation that you suggested.
I had actually come across this function definition/expression identity of Firefox that you pointed out, under a different context. You can add a
window.onloadhandler by simply defining a global function called “onload” on Firefox:function onload() { alert(42); }April 1st, 2009 at 6:28 pm
I just stumbled on this problem as well. At the root of the matter is the following: function definitions are not allowed inside ‘with’ statements or ‘catch’ blocks, according to the ECMAScript standard (262, 3rd edition). I’ll give a bit of detail below in the hopes it can be helpful to someone else.
In the ECMAScript grammar, function declarations are only allowed to occur inside ‘SourceElement’. Thus, Chrome, Firefox, IE, Opera and Safari all diverge from the ECMAScript standard with specific regard to section 12.4 (’ExpressionStatement’). Strictly speaking, the function definition in your ‘with’ statement is invalid syntax and should cause a thrown exception.
The spec doesn’t allow functions to be defined in this way for good reason: the function’s scope could not possibly be known at the time the function object is created. This leads to all sorts of inconsistencies in how browsers implement this bug.
As you said, Firefox will do the best it can given the circumstances: define function ‘foo’ in the variable/activation object of the enclosing function (or the global object), and add ‘obj’ to the static scope chain used by ‘foo’. However, unlike proper function definitions, you will not be able to forward-reference ‘foo’. Again, forward references would be problematic because the scope used by ‘foo’ hasn’t been defined yet.
The other browsers you mentioned will simply create the ‘foo’ function as if it were not contained in the ‘with’ statement. This technique allows forward-referencing ‘foo’, but as you’ve noticed this completely ignores the use of ‘with’ when invoking ‘foo’.
Finally, for something really strange you should see what IE does when including a function definition inside of a ‘catch’ block:
function x() { var ox = { vx: 2 }; var oxx = { vx: 3 }; try { throw oxx; } catch (ox) { function y () { ox.vx = -1; } }; y(); alert(ox.vx); alert(oxx.vx); } x();Chrome, Firefox, Opera and Safari use the same approach as for ‘with’. However, upon invoking ‘y’, IE will modify both ‘ox.vx’ and ‘oxx.vx’ (changing them each to -1).
I’m not sure what is happening in this case, but it goes to show this is one instance where the standard writers got it right and browser implementations didn’t take the advice.