Thursday, June 18th, 2009
Named function expressions in incredible detail
<p>The intrepid JS hacker Juriy "kangax" Zaytsev has an incredibly detailed post on demystifying named function expressions:Surprisingly, a topic of named function expressions doesn’t seem to be covered well enough on the web. This is probably why there are so many misconceptions floating around. In this article, I’ll try to summarize both - theoretical and practical aspects of these wonderful Javascript constructs; the good, bad and ugly parts of them.
In a nutshell, named function expressions are useful for one thing only - descriptive function names in debuggers and profilers. Well, there is also a possibility of using function names for recursion, but you will soon see that this is often impractical nowadays. If you don’t care about debugging experience, you have nothing to worry about. Otherwise, read on to see some of the cross-browser glitches you would have to deal with and tips on how work around them.
He then goes into a ton of examples of weirdness in different browsers, and fun code like this:
-
-
var f = function g() {
-
return 1;
-
};
-
if (false) {
-
f = function g(){
-
return 2;
-
}
-
};
-
g(); // 2
-
and
-
-
var f = function g(){
-
return [
-
arguments.callee == f,
-
arguments.callee == g
-
];
-
};
-
f(); // [true, false]
-
Finally, he shows a couple of techniques for using these correctly in the real world need of shimming:
-
-
// 1) enclose declaration with a separate scope
-
var addEvent = (function(){
-
-
var docEl = document.documentElement;
-
-
// 2) declare a variable to assign function to
-
var fn;
-
-
if (docEl.addEventListener) {
-
-
// 3) make sure to give function a descriptive identifier
-
fn = function addEvent(element, eventName, callback) {
-
element.addEventListener(eventName, callback, false);
-
}
-
}
-
else if (docEl.attachEvent) {
-
fn = function addEvent(element, eventName, callback) {
-
element.attachEvent('on' + eventName, callback);
-
}
-
}
-
else {
-
fn = function addEvent(element, eventName, callback) {
-
element['on' + eventName] = callback;
-
}
-
}
-
-
// 4) clean up `addEvent` function created by JScript
-
// make sure to either prepend assignment with `var`,
-
// or declare `addEvent` at the top of the function
-
var addEvent = null;
-
-
// 5) finally return function referenced by `fn`
-
return fn;
-
})();
-
Related Content:











Most interesting ressource I’ve read this year.
Thanks kangax !
It’s too bad the nulling shown in step 4 actually frees up memory, looks like some serious overkill, garbage collectors that don’t free up the memory shown in step 4 have room for improvement.
Perhaps I’m misunderstanding, but why is example #1 unintuitive?
It seems to obey function level scoping. If I have:
function g() { return 1; }
if(false) {
function g() { return 2; }
}
Of course, you’d expect g() to return 2, JS doesn’t have a different scope for if statements. It’s identical to:
var g;
g = function() { return 1; }
g = function() { return 2; }
What am I missing?
@zachleat I think you missed a lot. Try reading the post again.
function g() { … } is a function declaration. While var g = function() { … } is an unnamed function expression.
.
Quote from kangax’s post:
“Another important trait of function declarations is that declaring them conditionally is non-standardized and varies across different environments. You should never rely on functions being declared conditionally and use function expressions instead.”
.
“Never do this! Some browsers will declare `foo` as the one returning ‘first’, while others – returning ‘second’ “
What zachleat says is true, if you’re familiar with the ECMA standard. The example,
function g() { return 1; }if(false) {
function g() { return 2; }
}
is technically a syntax error. Function declarations are evaluated prior to script execution, and multiple declarations of the same function in the same scope is invalid. Some engines favor the second declaration simply because handling of the error is not defined.
@eyelidlessness – I could be wrong, I have been known to mis-read spec on more than on occasion. But in ECMA 5 draft,
.
13 Function Declaration (see 10.6)
.
10.6 Declaration Binding Instantiation
.
funcAlreadyDeclared is the result of calling env’s HasBinding concrete method passing fn as the argument. If its not already declared it calls CreateMutableBinding(..). Then it calls SetMutableBinding(…)
.
It doesn’t seem to call CreateImmutableBinding().
.
Which part of the spec are you referencing?
Why do JS language designers are so ignorant of the prior art?
JavaScript is ought to have a variable binding construct, “let” or something. Then you wouldn’t need so much monkeying.
Actually, “var I = function(x){return x}” and “function I(x){return x}” have a subtle difference. The latter permits mutually recursive definitions, while the former is pretty limited in this respect.
Now, if we give people that “let” form (and also a “let*” in Scheme parlance), we don’t need to “mistify” ourselves with nonsense like the “named function expressions”.
@ywg
Thank you!
@zachleat
I think you’re missing the difference between declaration and expression. What @jdalton said should pretty much answer your question.
@eyelidlessness
I don’t see how two function declarations with same-named identifier is a syntax error in ES3. Could you point me to a particular section in the specs describing this?
Thanks guys, glad I asked!
@chiaroscuro:
http://wiki.ecmascript.org/doku.php?id=harmony:harmony
https://developer.mozilla.org/en/New_in_JavaScript_1.7#Block_scope_with_let
Also, named function expressions are necessary for recursion since arguments.callee is being removed.