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:
#For 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:
#Thanks 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:
#Thanks 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:
#@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:
#Well written article and an interesting subject.
Wilq32 said:
#I really enjoy reading this article. Usually i stops in the middle but this time i read all :) Cheers !:)
Gianni Chiappetta (gf3) said:
#That 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:
#Hi, 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:
#Forgot 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:
#Dmitry, 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:
#Hi.
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