Searching archives didn't give any relevant results, so I made a simple
test case to find out what exactly happens and which versions are affected.
First of all, 3.x (and later) does not exhibit a buggy behavior; It only
happens with 2.x series, and probably lower (I tested 2.0 - 2.0.4).
Moreover, the syntax of named function expression per se does not make
Safari error out. It is which expression a faulty construct is part of
that seems to make a difference.
For example, named function being on a right hand side of an assignment
expression "works" as expected -
var f = function g(){};
window.alert(1);
- will alert "1". `f` will also be declared and reference a function
object. At the same time, placing named function expression in a
grouping operator errors out -
(function f(){})();
window.alert(1);
- `alert` is never reached.
I wrote few tests which check named expressions being used as part of
different constructs (I will post a testcase if anyone is interested).
From what I can see, the only time when named function expression
"works" is when it is an AssignmentExpression. Everything else fails.
This means that something like -
(function(){
if (someCondition) {
return function f(){};
}
return function g(){};
})();
- will fail, while something like this, will work -
(function(){
var f;
if (someCondition) {
f = function foo(){};
}
else {
f = function bar(){};
}
return f;
})();
What's interesting is that knowing this, we can fool Safari and imitate
the former "return" version by changing expression in return statement
from Expression to AssignmentExpression -
(function(){
var f;
if (true) {
return (f = function foo(){});
}
else {
return (f = function bar(){});
}
})();
- and, indeed, it works as expected.
I would be interested to hear thoughts and/or experience of others
regarding this subject.
--
kangax
It's not just Safari that has issues with named function expressions.
Don't use them.
I'm aware of JScript bugs. Nevertheless, "naming" functions allows for a
much more productive debugging and profiling experience. If you know the
issues with named expressions, it's not difficult to take certain
precautions, and end up with a much more descriptive call stack.
I think this is where many libraries get it "wrong" by using anonymous
function expressions almost exclusively.
I'm still researching JScript memory issues but the pattern I came up
with so far is (with an example of a more or less generic `addEvent`
helper):
var docEl = document.documentElement;
...
var addEvent = (function(){
var f;
if (docEl.addEventListener) {
f = function addEvent(element, evType, callback) {
element.addEventListener(evType, callback, false);
}
}
else if (docEl.attachEvent) {
f = function addEvent(element, evType, callback) {
element.attachEvent("on" + evType, callback);
}
}
else {
f = function addEvent(element, evType, callback) {
element['on' + evType] = callback;
}
}
var addEvent = null;
return f;
})();
JScript's bug is taken care of with explicit memory release before
returning from the outer fucntion's execution context (to prevent a
function - parsed erroneously by JScript as a function declaration -
from being "trapped" in a closure). My memory tests confirm this as well.
Identifier leakage into the enclosing scope is irrelevant here for
obvious reasons. Safari bug won't be exhibited either, as we know by now.
Btw, PointedEars' dhtml.js [1] seems to be using named function
expressions in *return statements*. It looks like the whole thing will
fail in Safari 2.0.2 and earlier.
[1] http://pointedears.de/scripts/dhtml.js
--
kangax