What does it mean when a variable equals a function? [duplicate] - javascript

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
JavaScript: var functionName = function() {} vs function functionName() {}
In JavaScript, what's the purpose of defining a variable as a function? I've seen this convention before and don't fully understand it.
For example, at some point in a script, a function is called like this:
whatever();
But where I would expect to see a function named whatever, like this:
function whatever(){
}
Instead I'll see a variable called whatever that's defined as a function, like this:
var whatever = function(){
}
What's the purpose of this? Why would you do this instead of just naming the function?

Note: Please see the update at the end of the answer, declarations within blocks became valid (but quite complicated if you're not using strict mode).
Here's one reason:
var whatever;
if (some_condition) {
whatever = function() {
// Do something
};
}
else {
whatever = function() {
// Do something else
};
}
whatever();
You might see code like that in the initialization of a library that has to handle implementation differences (such as differences between web browsers, a'la IE's attachEvent vs. the standard addEventListener). You cannot do the equivalent with a function declaration:
if (some_condition) {
function whatever() { // <=== DON'T DO THIS
// Do something
}
}
else {
function whatever() { // <=== IT'S INVALID
// Do something else
}
}
whatever();
...they're not specified within control structures, so JavaScript engines are allowed to do what they want, and different engines have done different things. (Edit: Again, see note below, they're specified now.)
Separately, there's a big difference between
var whatever = function() {
// ...
};
and
function whatever() {
// ...
}
The first is a function expression, and it's evaluated when the code reaches that point in the step-by-step execution of the context (e.g., the function it's in, or the step-by-step execution of global code). It also results in an anonymous function (the variable referring to it has a name, but the function does not, which has implications for helping your tools to help you).
The second is a function declaration, and it's evaluated upon entry to the context, before any step-by-step code is executed. (Some call this "hoisting" because something further down in the source happens earlier than something higher up in the source.) The function is also given a proper name.
So consider:
function foo() {
doSomething();
doSomethingElse();
console.log("typeof bar = " + typeof bar); // Logs "function"
function bar() {
}
}
whereas
function foo() {
doSomething();
doSomethingElse();
console.log("typeof bar = " + typeof bar); // Logs "undefined"
var bar = function() {
};
}
In the first example, with the declaration, the declaration is processed before the doSomething and other stepwise code is run. In the second example, because it's an expression, it's executed as part of the stepwise code and so the function isn't defined up above (the variable is defined up above, because var is also "hoisted").
And winding up: For the moment, you can't do this in general client-side web stuff:
var bar = function foo() { // <=== Don't do this in client-side code for now
// ...
};
You should be able to do that, it's called a named function expression and it's a function expression that gives the function a proper name. But various JavaScript engines at various times have gotten it wrong, and IE continued to get very wrong indeed until very recently.
Update for ES2015+
As of ES2015 (aka "ES6"), function declarations within blocks were added to the specification.
Strict mode
In strict mode, the newly-specified behavior is simple and easy to understand: They're scoped to the block in which they occur, and are hoisted to the top of it.
So this:
"use strict";
if (Math.random() < 0.5) {
foo();
function foo() {
console.log("low");
}
} else {
foo();
function foo() {
console.log("high");
}
}
console.log(typeof foo); // undefined
(Note how the calls to the functions are above the functions within the blocks.)
...is essentially equivalent to this:
"use strict";
if (Math.random() < 0.5) {
let foo = function() {
console.log("low");
};
foo();
} else {
let foo = function() {
console.log("high");
};
foo();
}
console.log(typeof foo); // undefined
Loose mode
Loose mode behavior is much more complex and moreover in theory it varies between JavaScript engines in web browsers and JavaScript engines not in web browsers. I won't get into it here. Just don't do it. If you insist on function declarations within blocks, use strict mode, where they make sense and are consistent across environments.

this is so you can store functions in variables and e.g. pass them to other functions as parameters. One example where this is usefull is in writing asynchronous functions which are passed callbacks as arguments
var callback = function() { console.log('done', result)}
var dosomething = function(callback) {
//do some stuff here
...
result = 1;
callback(result);
}
Since functions are objects in javascript you can extend them with properties and methods as well.

Functions in JavaScript are objects; they're values, in other words. Thus you can always set a variable to refer to a function regardless of how the function is defined:
function foo() { ... }
var anotherFoo = foo;
anotherFoo(); // calls foo
Functions are values that can be used as object properties, function parameters, array elements, and anything else a general value can do in JavaScript. They're objects and can have their own properties too.

When you assign a function to a variable, you can then pass it around as an argument to other functions, and also extend it to make use of Javascript's Object model.

If you declare a functionvariable, using "var", within a function, the variable can only be accessed within that function. When you exit the function, the variable is destroyed. These variables are called local variables. You can have local variables with the same name in different functions, because each is recognized only by the function in which it is declared.

Related

Difference between req.param = function(){} and req.param = function param() {} [duplicate]

We have two different way for doing function expression in JavaScript:
Named function expression (NFE):
var boo = function boo () {
alert(1);
};
Anonymous function expression:
var boo = function () {
alert(1);
};
And both of them can be called with boo();. I really can't see why/when I should use anonymous functions and when I should use Named Function Expressions. What difference is there between them?
In the case of the anonymous function expression, the function is anonymous — literally, it has no name. The variable you're assigning it to has a name, but the function does not. (Update: That was true through ES5. As of ES2015 [aka ES6], often a function created with an anonymous expression gets a true name [but not an automatic identifier], read on...)
Names are useful. Names can be seen in stack traces, call stacks, lists of breakpoints, etc. Names are a Good Thing™.
(You used to have to beware of named function expressions in older versions of IE [IE8 and below], because they mistakenly created two completely separate function objects at two completely different times [more in my blog article Double take]. If you need to support IE8 [!!], it's probably best to stick with anonymous function expressions or function declarations, but avoid named function expressions.)
One key thing about a named function expression is that it creates an in-scope identifier with that name for the function within the functon body:
var x = function example() {
console.log(typeof example); // "function"
};
x();
console.log(typeof example); // "undefined"
As of ES2015, though, a lot of "anonymous" function expressions create functions with names, and this was predated by various modern JavaScript engines being quite smart about inferring names from context. In ES2015, your anonymous function expression results in a function with the name boo. However, even with ES2015+ semantics, the automatic identifier is not created:
var obj = {
x: function() {
console.log(typeof x); // "undefined"
console.log(obj.x.name); // "x"
},
y: function y() {
console.log(typeof y); // "function"
console.log(obj.y.name); // "y"
}
};
obj.x();
obj.y();
The assignment fo the function's name is done with the SetFunctionName abstract operation used in various operations in the spec.
The short version is basically any time an anonymous function expression appears on the right-hand side of something like an assignment or initialization, like:
var boo = function() { /*...*/ };
(or it could be let or const rather than var), or
var obj = {
boo: function() { /*...*/ }
};
or
doSomething({
boo: function() { /*...*/ }
});
(those last two are really the same thing), the resulting function will have a name (boo, in the examples).
There's an important, and intentional, exception: Assigning to a property on an existing object:
obj.boo = function() { /*...*/ }; // <== Does not get a name
This was because of information leak concerns raised when the new feature was going through the process of being added; details in my answer to another question here.
Naming functions is useful if they need to reference themselves (e.g. for recursive calls). Indeed, if you are passing a literal function expression as an argument directly to another function, that function expression cannot directly reference itself in ES5 strict mode unless it is named.
For example, consider this code:
setTimeout(function sayMoo() {
alert('MOO');
setTimeout(sayMoo, 1000);
}, 1000);
It would be impossible to write this code quite this cleanly if the function expression passed to setTimeout were anonymous; we would need to assign it to a variable instead prior to the setTimeout call. This way, with a named function expression, is slightly shorter and neater.
It was historically possible to write code like this even using an anonymous function expression, by exploiting arguments.callee...
setTimeout(function () {
alert('MOO');
setTimeout(arguments.callee, 1000);
}, 1000);
... but arguments.callee is deprecated, and is outright forbidden in ES5 strict mode. Hence MDN advises:
Avoid using arguments.callee() by either giving function expressions a name or use a function declaration where a function must call itself.
(emphasis mine)
You should always use named function expressions, that's why:
You can use the name of that function when you need recursion.
Anonymous functions doesn't help when debugging as you can't see the name of the function that causes problems.
When you do not name a function, later on its harder to understand what it's doing. Giving it a name makes it easier to understand.
var foo = function bar() {
//some code...
};
foo();
bar(); // Error!
Here, for example, because the name bar is used within a function expression, it doesn't get declared in the outer scope. With named function expressions, the name of the function expression is enclosed within its own scope.
If a function is specified as a Function Expression, it can be given a name.
It will only be available inside the function (except IE8-).
var f = function sayHi(name) {
alert( sayHi ); // Inside the function you can see the function code
};
alert( sayHi ); // (Error: undefined variable 'sayHi')
This name is intended for a reliable recursive function call, even if it is written to another variable.
In addition, the NFE (Named Function Expression) name CAN be overwritten with the Object.defineProperty(...) method as follows:
var test = function sayHi(name) {
Object.defineProperty(test, 'name', { value: 'foo', configurable: true });
alert( test.name ); // foo
};
test();
Note: that with the Function Declaration this can not be done. This "special" internal function name is specified only in the Function Expression syntax.
Using named function expressions is better, when you want to be able to reference the function in question without having to rely on deprecated features such as arguments.callee.

What's the difference between naming a function (as opposed to leaving it anon) and creating a reference to it?

In a comment on another thread I started, someone said this:
#adlwalrus yes. try this: var foo = function bar(){}; console.log(foo); But be aware that bar is only function name (what does it mean I'm not sure exactly myself) and not a reference to it, so you can't call it by doing bar(). And assigning (even named function) is not the same as declaring a function. Hoisting (bumping to top of the scope) only works for declarations, assignment will stay in place. – valentinas 6 hours ago
What purpose does a function name serve if you can't call it with bar()?
For the function to call itself.
var x = function y(val){
if (val){
console.log(val);
y(val-1);
}
};
x(5);
> 3
> 2
> 1
y(3);
> ReferenceError: y is not defined
You're referring to a named function expression. The spec requires that the name of such functions only be available within the scope of the new function. Spec quote:
The Identifier in a FunctionExpression can be referenced from inside
the FunctionExpression's FunctionBody to allow the function to call
itself recursively. However, unlike in a FunctionDeclaration, the
Identifier in a FunctionExpression cannot be referenced from and does
not affect the scope enclosing the FunctionExpression.
On the other hand, the result of that expression is a reference to the new function, which can be saved and referenced anywhere.
Lots of details here:
http://kangax.github.com/nfe/#named-expr
I'd read the whole thing.
As for benefits, another is that it makes them easier to identify in a debugger.
There are two ways to create a function in JavaScript, a "function declaration" and a "function expression." I believe it was Doug Crockford who explained it best when he pointed out that unless "function" is the very first set of characters on a given line, you're performing a function expression (not a declaration).
Function declarations are finicky creatures. You'll recognize them when you see them. They look like this:
function foo() { /* ... */ }
They're always given a name (it's requited) and the name is locally scoped to the lexical context under which the function is declared. So if you perform a function declaration in the global context, then the function can be referenced via it's name globally. If you do it within a function, the function's name can be referenced only within that function and any functions declared within that function.
I think the most important aspect of this method of declaring a function (one that is rarely commented on) is that the function initialization gets hoisted to the top of the current lexical context. Therefore, you should never, ever use a function declaration within a conditional, such as this:
//DON'T DO THIS!
if (x) {
function foo() { return 1; }
} else {
function foo() { return 2; }
}
foo(); //will always be 2, regardless of the value of x.
A function expression is slightly different. Often, they're directly assigned to a variable, like so:
var foo = function() { /* ... */ };
This is nearly identical to the function declaration above except that the initialization is not hoisted. So you can do the following:
var foo;
if (x) {
foo = function() { return 1; };
} else {
foo = function() { return 2; };
}
foo(); //will be 1 or 2, depending on the truthy-ness of x.
So, back to the original question. Function expressions can also have a name, though it's not required and it's not scoped to the context in which the function is declared (as with function declarations). Instead, it gets scoped to the function's own lexical context. This is very useful in some cases. My personal favorite is this pattern:
(function foo() {
//Do something.
setTimeout(foo, 1000);
}());
foo; //undefined
Because of the parenthesis before the word "function", this is a function expression and the name is scoped internally only. But that's okay, because we only need to call it internally (via setTimeout()). The result is that the function will execute once immediately, then will re-execute every second or so after it's finishes execution. This is safer than using setInterval() because it will wait until it's done executing before rescheduling itself, preventing overlaps that could cause missed executions and/or "domination" of the JavaScript thread.
Essentially, the use of a named function expression is limited, but when you need it, it's very powerful.

Why would I assign a function declaration to a named variable?

Edit: it's NOT an assignment of a function declaration to a named variable - check the accepted answer. Leaving title as it is because other people might make the same error as me.
While reading Paul Irish's infinitescroll jquery plugin code, I stumbled again and again over the following pattern:
...
_create : function infscr_create (options, callback) { /* ... */ },
...
What is the benefit of doing this instead of:
...
_create : function (options, callback) { /* ... */ },
...
The benefit of that (which is called a "named function expression") is that the function has an actual name. In your second version, the property has a name, but the function doesn't. Giving functions actual names helps your tools help you (call stack listings, breakpoint listings, etc.) More: Anonymouses anonymous
The disadvantage to it is that it has unexpected results in some broken JavaScript engines, like the one in IE8 and earlier. In IE8 and earlier, Paul Irish's version creates two separate functions at two completely different times. But it's not really a problem unless you keep and use references to both of them, and expect them to be the same function (for instance, when hooking up and unhooking event handlers). Given it's Paul, I'm guessing he's being sure not to do that.
Re your question title: Note that it's not a function declaration, but you can be forgiven for thinking it is, as it looks almost exactly like one. :-) It's a function expression. Function declarations and function expressions happen at completely different times, and have different impacts on the scope in which they're created.
Just for completeness:
// This is a function declaration -- note that it's not a "right-hand
// value", e.g., we're not using the result of it immediately (via an
// assignment, a property initializer, calling it, or passing it into
// a function as an argument -- none of those).
//
// Declarations happen upon entry to the scope (not as part of step-by-
// step code). The function's name is added to the scope in which it's
// declared. Declarations are illegal inside branches (`if`, `try/catch`,
// `for`, etc.), but some engines will rewrite them as expressions for
// you if you do that. Others will not, they'll just always declare the
// function regardless of whether the code was reached. So don't do that.
function foo() {
}
// These are all anonymous function expressions. The function in the
// expression has no name, although some debuggers are pretty smart
// about looking at the expression and (where they can) listing a
// kind of pseudo-name for the function. Others are not that smart,
// which is why I avoid anonymous functions.
//
// Expressions happen when they're reached in step-by-step code.
var f = function() { };
var obj = {
prop: function() { }
};
doSomethingCoolWithAFunction(function() { });
(function() { })(); // Call it immediately
!function() { }(); // Call it immediately
~function() { }(); // Call it immediately, there are a few variants
// These are all *named* function expressions.
//
// Since they're expressions, they happen when they're reached in the
// step-by-step code. The function's name is NOT added to the containing
// scope (except by engines with bugs).
//
// These are the same examples as above, but with a name. No other changes.
var f = function foo() { };
var obj = {
prop: function foo() { }
};
doSomethingCoolWithAFunction(function foo() { });
(function foo() { })(); // Call it immediately
!function foo() { }(); // Call it immediately
~function foo() { }(); // Call it immediately, there are a few variants
The function has a name, rather than being an anonymous function; this shows up in debug traces, making debugging easier.
The function can invoke itself using by calling infscr_create()

Are named functions preferred over anonymous functions in JavaScript? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
JavaScript: var functionName = function() {} vs function functionName() {}
There are two possible methods for pulling out a function in Javascript:
var foo = function() { ... }
This is a bit contrived; another common pattern is:
var foo = {
baz: 43,
doSomething: function() {
// ...
}
}
versus
function foo() {
// ...
}
Is there an explicit reason to prefer one or the other?
It all comes down to preference to where you declare your functions; hoisting.
Function declarations and variable declarations are always moved ("hoisted") invisibly to the top of their containing scope by the JavaScript interpreter. Function parameters and language-defined names are, obviously, already there. This means that code like this:
function foo() {
bar();
var x = 1;
}
is actually interpreted like this:
function foo() {
var x;
bar();
x = 1;
}
Notice that the assignment portion of the declarations were not hoisted. Only the name is hoisted. This is not the case with function declarations, where the entire function body will be hoisted as well.
function test() {
foo(); // TypeError "foo is not a function"
bar(); // "this will run!"
var foo = function () { // function expression assigned to local variable 'foo'
alert("this won't run!");
}
function bar() { // function declaration, given the name 'bar'
alert("this will run!");
}
}
test();
In this case, only the function declaration has its body hoisted to the top. The name 'foo' is hoisted, but the body is left behind, to be assigned during execution.
You can give names to functions defined in function expressions, with syntax like a function declaration. This does not make it a function declaration, and the name is not brought into scope, nor is the body hoisted.
foo(); // TypeError "foo is not a function"
bar(); // valid
baz(); // TypeError "baz is not a function"
bin(); // ReferenceError "bin is not defined"
var foo = function () {}; // anonymous function expression ('foo' gets hoisted)
function bar() {}; // function declaration ('bar' and the function body get hoisted)
var baz = function bin() {}; // named function expression (only 'baz' gets hoisted)
foo(); // valid
bar(); // valid
baz(); // valid
bin(); // ReferenceError "bin is not defined"
So, if your preference is to have functions hoist to the top use a function declaration otherwise use expression. I prefer the latter as I typically build object literals with methods as function expressions.
Named function expressions can be handy when errors are thrown. The console will tell you what the function is instead of stating anonymous aka stack trace.
You've hit on a couple different things here, but I'll try to hit your main question first.
In general....
function() { ... } is a function expression. Syntaxically this is on the same level as 2 or [4,5]. This represents a value. So doing var foo=function(){ ... } will work as planned, every time.
function foo() { ... } is a function declaration. This might seem to do the same thing as var foo=function(){...}, but there's a small caveat. As its a declaration, it works similar to the concept of variable hoisting in JS (basically, all variable declarations are done before any expressions are evaluated).
A good example is from here:
function test() {
foo(); // TypeError "foo is not a function"
bar(); // "this will run!"
var foo = function () { // function expression assigned to local variable 'foo'
alert("this won't run!");
}
function bar() { // function declaration, given the name 'bar'
alert("this will run!");
}
}
test();
Basically variable hoisting has brought the value up to the top, so this code is equivalent (in theory) to :
function test() {
var foo;//foo hoisted to top
var bar=function(){//this as well
alert("this will run!");
}
foo(); // TypeError "foo is not a function"
bar(); // "this will run!"
var foo = function () { // function expression assigned to local variable 'foo'
alert("this won't run!");
}
}
NB: I'd like to take this spot to say that JS interpreters have a hard time following theory, so trusting them on somewhat iffy behaviour is not recommended. Here you'll find a good example at the end of a section where theory and practice end up not working (there are also some more details on the topic of expressions vs declarations).
Fun fact: wrapping function foo() {...} in parentheses transforms it from a declaration to an expression, which can lead to some weird looking code like
(function foo() { return 1; })();// 1
foo; //ReferenceError: foo is not defined
Don't do this if you don't have a reason to, please.
Summary var foo=function(){ ... } is *sorta kinda * the same as function foo(){ ... } except that the former does what you think it does where you think it should whereas the latter does weird stuff unless you wrap it in parens, but that messes up the scope, and JS interpreters allow you to do things that are considered syntax errors in the spec so you're led to believe that wrong things are in fact right, etc....
please use function expressions( var f=function(){...} ). There's no real reason not to, especially considering you're somewhat forced to do it when you're using dot syntax.
On to the second thing you touched.....
I'm not really sure what to say, it's kinda sorta completely different from everything else about this.
var foo = {
baz: 43,
doSomething:function() {
...
}
}
this is known as object literal syntax. JSON, which is based off of this syntax, is a pretty neat way of formatting data, and this syntax in JS is often used to declare new objects, with singleton objects for example(avoiding all the mess with declaring a function and using new ). It can also be used in the same way XML is used, and is preferred by all the cool kids...
Anyways, basically object literal syntax works like this:
{ name1: val1, .... namek:valk }
This expression is an object with certain values initialised on it. so doing var obj={ name1: val1, .... namek:valk } means that :
obj.name1==val1;
obj['name1']==val1;// x['y'] is the same thing as x.y
...
obj.namek==valk;
So what does this have to do with our example? Basically your expression is often used to declare singleton objects. But it can also be used to declare an object prototype, so someone can later do var newObj=Object.create(foo) , and newObj will have foo as a prototype.
Look into prototypal inheritence in detail if you want to really get how useful it is. Douglas Crockford talks about it in detail in one of his many talks).
There are few advantages to naming functions
names for meta analysis. functionInstance.name will show you the name.
Far more importantly, the name will be printed in stack traces.
names also help write self documenting or literate code.
There is a single disadvantage to named functions expressions
IE has memory leaks for NFE
There are no disadvantages to function declarations apart from less stylistic control
Your question really comprises of two parts, as you don't necessarily have to make your functions anonymous if they are assigned to a variable or property.
Named vs anonymous?
#Raynos highlights the main points clearly. The best part about named functions is that they will show themselves in a stack trace. Even in situations where functions are being assigned to variables/properties, it's a good idea to give your functions a name just to aid with debugging, however I wouldn't say anonymous functions are evil at all. They do serve a fine purpose:
Are anonymous functions a bad practice in JavaScript?
Function declaration vs function expression?
For that part of the question I would refer you to this question as it probably covers the topic in far more depth than I can
var functionName = function() {} vs function functionName() {}

Named Function Expressions in IE, part 2

I asked this question a while back and was happy with the accepted answer. I just now realized, however, that the following technique:
var testaroo = 0;
(function executeOnLoad() {
if (testaroo++ < 5) {
setTimeout(executeOnLoad, 25);
return;
}
alert(testaroo); // alerts "6"
})();
returns the result I expect. If T.J.Crowder's answer from my first question is correct, then shouldn't this technique not work?
A very good question. :-)
The difference:
The difference between this and your detachEvent situation is that here, you don't care that the function reference inside and outside "the function" is the same, just that the code be the same. In the detachEvent situation, it mattered that you see the same function reference inside and outside "the function" because that's how detachEvent works, by detaching the specific function you give it.
Two functions?
Yes. CMS pointed out that IE (JScript) creates two functions when it sees a named function expression like the one in your code. (We'll come back to this.) The interesting thing is that you're calling both of them. Yes, really. :-) The initial call calls the function returned by the expression, and then all of the calls using the name call the the other one.
Modifying your code slightly can make this a bit clearer:
var testaroo = 0;
var f = function executeOnLoad() {
if (testaroo++ < 5) {
setTimeout(executeOnLoad, 25);
return;
}
alert(testaroo); // alerts "6"
};
f();
The f(); at the end calls the function that was returned by the function expression, but interestingly, that function is only called once. All the other times, when it's called via the executeOnLoad reference, it's the other function that gets called. But since the two functions both close over the same data (which includes the testaroo variable) and they have the same code, the effect is very like there being just one function. We can demonstrate there are two, though, much the way CMS did:
var testaroo = 0;
var f = function executeOnLoad() {
if (testaroo++ < 5) {
setTimeout(executeOnLoad, 0);
return;
}
alert(testaroo); // alerts "6"
// Alerts "Same function? false"
alert("Same function? " + (f === executeOnLoad));
};
f();
This isn't just some artifact of the names, either, there really are two functions being created by JScript.
What's going on?
Basically, the people implementing JScript apparently decided to process named function expressions both as function declarations and as function expressions, creating two function objects in the process (one from the "declaration," one from the "expression") and almost certainly doing so at different times. This is completely wrong, but it's what they did. Surprisingly, even the new JScript in IE8 continues this behavior.
That's why your code sees (and uses) two different functions. It's also the reason for the name "leak" that CMS mentioned. Shifting to a slightly modified copy of his example:
function outer() {
var myFunc = function inner() {};
alert(typeof inner); // "undefined" on most browsers, "function" on IE
if (typeof inner !== "undefined") { // avoid TypeError on other browsers
// IE actually creates two function objects: Two proofs:
alert(inner === myFunc); // false!
inner.foo = "foo";
alert(inner.foo); // "foo"
alert(myFunc.foo); // undefined
}
}
As he mentioned, inner is defined on IE (JScript) but not on other browsers. Why not? To the casual observer, aside from the two functions thing, JScript's behavior with regard to the function name seems correct. After all, only functions introduce new scope in Javascript, right? And the inner function is clearly defined in outer. But the spec actually went to pains to say no, that symbol is not defined in outer (even going so far as to delve into details about how implementations avoid it without breaking other rules). It's covered in Section 13 (in both the 3rd and 5th edition specs). Here's the relevant high-level quote:
The Identifier in a FunctionExpression can be referenced from inside the FunctionExpression's FunctionBody to allow the function to call itself recursively. However, unlike in a FunctionDeclaration, the Identifier in a FunctionExpression cannot be referenced from and does not affect the scope enclosing the FunctionExpression.
Why did they go to this trouble? I don't know, but I suspect it relates to the fact that function declarations are evaluated before any statement code (step-by-step code) is executed, whereas function expressions — like all expressions — are evaluated as part of the statement code, when they're reached in the control flow. Consider:
function foo() {
bar();
function bar() {
alert("Hi!");
}
}
When the control flow enters function foo, one of the first things that happens is that the bar function is instantiated and bound to the symbol bar; only then does the interpreter start processing the statements in foo's function body. That's why the call to bar at the top works.
But here:
function foo() {
var f;
f = function() {
alert("Hi!");
};
f();
}
The function expression is evaluated when it's reached (well, probably; we can't be sure some implementations don't do it earlier). One good reason the expression isn't (or shouldn't be) evaluated earlier is:
function foo() {
var f;
if (some_condition) {
f = function() {
alert("Hi (1)!");
};
}
else {
f = function() {
alert("Hi! (2)");
};
}
f();
}
...doing it earlier leads to ambiguity and/or wasted effort. Which leads to the question of what should happen here:
function foo() {
var f;
bar();
if (some_condition) {
f = function bar() {
alert("Hi (1)!");
};
}
else {
f = function bar() {
alert("Hi! (2)");
};
}
f();
}
Which bar gets called at the beginning? The way the specification authors chose to address that situation was to say that bar is not defined in foo at all, hence side-stepping the issue entirely. (It's not the only way they could have addressed it, but it seems to be the way they chose to do so.)
So how does IE (JScript) process that? The bar called at the beginning alerts "Hi (2)!". This, combined with the fact we know two function objects are created based on our other tests, is the clearest indication that JScript processes named function expressions as function declarations and function expressions, because that's exactly what is supposed to happen here:
function outer() {
bar();
function bar() {
alert("Hi (1)!");
}
function bar() {
alert("Hi (2)!");
}
}
There we have two function declarations with the same name. Syntax error? You'd think so, but it isn't. The specification clearly allows it, and says that the second declaration in source code order "wins." From Section 10.1.3 of the 3rd edition spec:
For each FunctionDeclaration in the code, in source text order, create a property of the variable object whose name is the Identifier in the FunctionDeclaration...If the variable object already has a property with this name, replace its value and attributes...
(The "variable object" is how symbols get resolved; that's a whole 'nother topic.) It's just as unambiguous in the 5th edition (Section 10.5), but, um, a lot less quotable.
So it's just IE, then?
Just to be clear, IE isn't the only browser that has (or had) unusual handling of NFEs, although they're getting pretty lonely (a pretty big Safari issue has been fixed, for instance). It's just that JScript has a really big quirk in this regard. But come to that, I think it actually is the only current major implementation with any really big issue — be interested to know of any others, if anyone knows of them.
Where we stand
Given all of the above, for the moment, I stay away from NFEs because I (like most people) have to support JScript. After all, it's easy enough to use a function declaration and then refer to it later (or indeed, earlier) with a variable:
function foo() { }
var f = foo;
...and that works reliably across browsers, avoiding issues like your detachEvent problem. Other reasonable people solve the problem differently, just accepting that two functions will get created and trying to minimize the impact, but I don't like that answer at all because of exactly what happened to you with detachEvent.
Well, it will work, the problem with JScript (IE), is that the identifier of the function expression (executeOnLoad) will leak to its enclosing scope, and actually creating two function objects..
(function () {
var myFunc = function foo () {};
alert(typeof foo); // "undefined" on all browsers, "function" on IE
if (typeof foo !== "undefined") { // avoid TypeError on other browsers
alert( foo === myFunc ); // false!, IE actually creates two function objects
}
})();

Categories