Thursday, June 18th, 2009

Named function expressions in incredible detail

Category: Articles, JavaScript

<>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:

javascript
< view plain text >
  1. var f = function g() {
  2.         return 1;
  3.     };
  4.     if (false) {
  5.         f = function g(){
  6.             return 2;
  7.         }
  8.     };
  9.     g(); // 2

and

javascript
< view plain text >
  1. var f = function g(){
  2.     return [
  3.       arguments.callee == f,
  4.       arguments.callee == g
  5.     ];
  6.   };
  7.   f(); // [true, false]

Finally, he shows a couple of techniques for using these correctly in the real world need of shimming:

javascript
< view plain text >
  1. // 1) enclose declaration with a separate scope
  2.   var addEvent = (function(){
  3.  
  4.     var docEl = document.documentElement;
  5.  
  6.     // 2) declare a variable to assign function to
  7.     var fn;
  8.  
  9.     if (docEl.addEventListener) {
  10.  
  11.       // 3) make sure to give function a descriptive identifier
  12.       fn = function addEvent(element, eventName, callback) {
  13.         element.addEventListener(eventName, callback, false);
  14.       }
  15.     }
  16.     else if (docEl.attachEvent) {
  17.       fn = function addEvent(element, eventName, callback) {
  18.         element.attachEvent('on' + eventName, callback);
  19.       }
  20.     }
  21.     else {
  22.       fn = function addEvent(element, eventName, callback) {
  23.         element['on' + eventName] = callback;
  24.       }
  25.     }
  26.  
  27.     // 4) clean up `addEvent` function created by JScript
  28.     //    make sure to either prepend assignment with `var`,
  29.     //    or declare `addEvent` at the top of the function
  30.     var addEvent = null;
  31.  
  32.     // 5) finally return function referenced by `fn`
  33.     return fn;
  34.   })();

Related Content:

  • XSLT expressions
    In the fifth part of his XSLT tutorial, Ed Tittel looks at XSLT expressions – the "transformation engine" behind XSLT; he also sums up a list of...
  • XSLT expression variables and data types
    In the fourth part of his XSLT tutorial, Ed Tittel covers XSLT variables and data types used in XSLT expressions and introduces you to XPath, the...
  • Reviewing bluelock vCloud Express
    Using blueblock vCloud Express is a fairly good experience. The user interface (UI) is clean and fast, virtual machines (VMs) spin up quickly, and the...
  • SQL Server 2005 Express: Beyond the basics
    It might be a scaled down version of SQL Server, but SQL Server 2005 Express Edition delivers advanced performance. Windows expert Serdar Yegulalp...
  • Understanding SQL string functions
    Each week, SearchOracle.com will bring you tips and examples of one SQL string function, such as CHAR, CONCAT and REPLACE, in this chapter excerpt...

Posted by Dion Almaer at 6:22 am
10 Comments

++++-
4.2 rating from 38 votes

10 Comments »

Comments feed TrackBack URI

Most interesting ressource I’ve read this year.
Thanks kangax !

Comment by ywg — June 18, 2009

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.

Comment by Jadet — June 18, 2009

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?

Comment by zachleat — June 18, 2009

@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’ “

Comment by jdalton — June 18, 2009

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.

Comment by eyelidlessness — June 18, 2009

@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?

Comment by jdalton — June 18, 2009

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”.

Comment by chiaroscuro — June 18, 2009

@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?

Comment by kangax — June 18, 2009

Thanks guys, glad I asked!

Comment by zachleat — June 19, 2009

@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.

Comment by TNO — June 19, 2009

Leave a comment

You must be logged in to post a comment.