Named function expressions demystified
After a couple of months, I have finally finished an article on named function expressions. It’s meant to demystify some of the common misconceptions I’ve seen on the web, take an in-depth look at cross-browser quirks and explain how to safely “work around” them. Quite obviously, it also explains what named function expressions are good for and how to “take advantage” of them in your applications.
If you have suggestions or find any mistakes, please let me know. Hope you like it.
Christof said on Jun 16, 2009 @ 11:39
#1For a solution of name leaking and other stuff in IE would it not simply be possible to use: var f = function f() {} ? This way the variable name “f” will overwrite the leaked function name “f”? I guess the problem with leaking memory may be still there (and no way to null f either) but as you wrote not a big problem for not too many functions.
unscriptable said on Jun 17, 2009 @ 19:22
#2Thanks for bringing this all together, kangax!
I can remember hitting your JScript bug #4 back in 2005 while trying to write a cross-browser XHR wrapper and spending days being bewildered why IE wasn’t working. I’ve definitely hit the Safari 2 bugs as well. I swear the following forces Safari 2.0 to bail, too, but I haven’t bothered to test it recently:
var obj = { f: function f () {} };Bertrand Le Roy said on Jun 18, 2009 @ 15:25
#3Thanks for the greatly documented article.
There is one particular common construct that doesn’t work in Safari 2 that we found we had to work around: if you want to set-up an object with named function fields (typically a prototype) like this: var a = {foo: function foo() {} }. You need to take the function out for it to work: function foo() {}; var a = {foo: foo}; Of course, var a = {foo: function() {} }; works everywhere (and that’s what we do in release mode) but you lose the benefits of named functions in debug mode.
kangax (article author) said on Jun 18, 2009 @ 20:32
#4@Christof
I remember I had similar idea and tests showed that memory leak was indeed less severe. I wasn’t very fond of such solution, as it seemed like it would make code more verbose.
Consider:
if (smth) { addEvent = function addEvent(){ ... }; } else if (smthElse) { addEvent = function addEvent(){ ... }; } else { addEvent = function addEvent(){ ... }; }I think it might be a good idea to mention this in the article. Thanks for your feedback!
@unscriptable
I remember hitting these bugs back in the days as well : ) Hopefully, this reference will make such experience less painful for future developers.
@unscriptable, @Bertrand
An example you mention seems to confirms my theory: that only function expressions that are part of AssignmentExpression‘s are parsed correctly.
I bet if you change it to something like this, it will actually work as expected:
var temp; ... var obj = { x: (temp = function x(){ ... }) ... } ... temp = null;(Essentially turning expression into assignment one)
nimmr said on Jun 19, 2009 @ 1:01
#5Well written article and an interesting subject.
Wilq32 said on Jun 19, 2009 @ 4:32
#6I really enjoy reading this article. Usually i stops in the middle but this time i read all :) Cheers !:)
Gianni Chiappetta (gf3) said on Jun 30, 2009 @ 21:15
#7That really cleared up some grey areas for me, thanks kangax. I’ll undoubtedly be referring to this article while writing future scripts, thanks for the great reference.
Dmitry A. Soshnikov said on Oct 21, 2009 @ 7:03
#8Hi, kangax! As I’ve already mentioned – thanks for a good article and useful comments for articles in my blog.
I also have some additions to this article – about NFE bugs in JScript.
But indeed arguments.callee references to that object which calls the function (or better to say functions – as there’re two function objects):
var f = function g(){
return [
arguments.callee == f,
arguments.callee == g
];
};
f(); // [true, false]
// but:
g(); // [false, true]
Moreover, if to call function right after creation, arguments.callee won’t be equal neither to [f] nor to [g]:
var f = function g(){
alert([
arguments.callee == f,
arguments.callee == g
]);
}(); // [false, false]
First [false] sure correct (as [f] is not assigned value yet which should be result of [g] – undefined), but the second one [false] – is again JScript’s bug and I guess [arguments.callee] in this moment references to that second function object.
I mentioned that in new section of my article – http://javascript.ru/blog/Dmitry-A.-Soshnikov/Tonkosti-ECMA-262-3.-CHast-5.-Funkcii.#nfe-i-jscript
Dmitry A. Soshnikov said on Oct 21, 2009 @ 7:17
#9Forgot this one (if you have premoderation of the comments you can join this comment and the previous one):
It’s actually related with assignment statement together with description function object. In this case really two different objects will be created. But this bug is not taking place if to create “NFE” (which better to say – FD in JScript) with e.g. grouping operator:
(function g(){}); // should be NFE, but in IE it looks like FD
var f = g;
f === g; // true
f.expando = ‘foo’;
g.expando; // ‘foo’
kangax (article author) said on Oct 30, 2009 @ 12:24
#10Dmitry, thanks for these additions.
I corrected
[true, false]/[false, true]example (although only in my local copy at the moment). An example with[false, false]return value (when function is being evaluated immediately) kind of makes sense. As you rightfully noticed, during evaluationfis not yet being assigned a value, andgactually references another function object — the one created during variable declaration phase : )As far as your
(function g(){})example, I think it can all be explained pretty simple as well. In a conforming environment,(function g(){})is a function expression that’s not being assigned to anything. It’s simply evaluated and can be discarded immediately (since no references to it exist).However, as we know, in JScript, that expression is parsed as plain function declaration and
gbecomes available to the entire scope. Later, at run time, this expression is being evaluated in JScript, and another function object is being created. This another function object is then immediately discarded just like it should.So, when you later assign that
gto another variable —f, they obviously end up referencing the very same function object — the one created during variable declaration, not the one created at run time (since that one was discarded immediately).Pierre Spring said on Jun 5, 2010 @ 15:58
#11Hi.
Thank you for this fantastic article. I learned a lot!
A tiny error happened in the title of the Example #4 of the JScript section, where you write “Function declaration” instead of “Named function expression”… But that is just a detail…
Yours … Pierre
esquifit said on Jan 1, 2012 @ 2:44
#12Thank you for the detailed explanation. There is statement though that seem not be quite right:
Function statements are NOT declared during variable instantiation. They are declared at run time, just like function expressions. However, once declared, function statement’s identifier becomes available to the entire scope of the function. I tested it with IE 8 and Firefox 8 and I got this in both cases:
typeof foo; // "function", not 'undefined' as stated.
if (true) {
// once block is entered, `foo` becomes declared and available to the entire scope
function foo(){ return 1; }
}
else {
// this block is never entered, and `foo` is never redeclared
function foo(){ return 2; }
}
Maybe I’m misinterpreting something?
witek said on Jan 1, 2012 @ 9:21
#13I think named function expression is not only usefull for debuging, but also for “anonymous” recursion. Normally in functional languages, it is done using Y-combinator, but is quite hard contecpt, by giving a function name which is only available inside a function it is simple to create almost-anonymous recursvie function.
var fact = (function f(n) {
if (n == 0) return 1;
return n*f(n-1);
});
You can safely change “var fact”, to something different (or use in different expression context), and it will still work!
Devin Rhode said on Dec 30, 2012 @ 17:20
#14Are named closures ok? Like this:
$el.click(function elOnClick() {
});
(function aFile() {
})();
Devin Rhode said on Dec 31, 2012 @ 11:09
#15So, generally, the more often you can use function declarations, the better, because you get function names without errors or memory leaks. (jQuery has about 55 function declarations!)
But – what’s an easy and clean way to expose a function for external use, but allow it to access some private variables in a library?
Here’s a pattern I _used_ to do:
var publicFn;
(function myLibrary() {
publicFn = function publicFn() {
};
})();
Seems like a better pattern would be:
var publicFn;
(function myLibrary() {
publicFn = (function(){
function publicFn() {
}
return publicFn;
})();
})();
For a library building up one object, like $ or _, the corresponding style would be:
var _;
(function underscoreJS(){
function _each(…) {
//…
}
_.each = _each;
})();
Correct?
Evgeni Petrov said on Mar 11, 2013 @ 3:13
#16Thank you.