views:

359

answers:

5

I am new to Javascript and got confused by how the function declaration works. I made some test on that and got some interesting results:

say();

function say()
{
    alert("say");
}

The forward-declaration worked and popup "say"

On the opposite

say();

say = function()
{
    alert("say");
}

did not work, although it also declared a function object

If we declare the function and redeclare that afterwards:

function say()
{
    alert("speak");
}

say();

function say()
{
    alert("say");
}

I got "say" instead of "speak". That's surprise!

OK. It seems that only the latest function declaration works. Then lets declare function object first and then a "regular" function:

say = function()
{
    alert("speak");
}

say();

function say()
{
    alert("say");
}

say();

Another surprise, it was "speak" followed by "speak". The "regular" function declaration did not work at all!

Is there an explanation of all of them? And, if the "regular" function declaration is really that "fragile" and can be easily override by the function object with same name, should I stay away from that?

Another question is: with only the function object format, does that forward-declaration become impossible? Is there any way to "simulate" that in Javascript?

A: 

You can expect your function definitions to be applied in order. Then all of your non-method lines of code will be executed in order, including the assignment of function objects. This explains each of your examples. It is an interesting issue though. You really can't assign a function object after attempting to call it, and expect it to work. However, a function definition that follows executable code will actually be applied first.

Eric Mickelsen
A: 
Kamiel Wanrooij
I see your points. But what's the scope if I define local function objects? I tried to call window.say() and the result is still not what I expected:<code>function say() { alert("say"); }window.say();</code>will popup "say"and <code>var say = function() { alert("speak"); }window.say();function say() { alert("say"); }window.say();</code>will popup "speak" twice. Does that mean both the "global" function and the "local" object are members of the window object?
Xiang
sorry I don't know how to format the comment... :(
Xiang
@Xiang: The reason why you get "speak" twice is that the JavaScript interpreter does two passes: in the first, it creates properties of the Variable object (which is used to look up variables in the current scope and is the global object in global code) for `var` statements and function declarations, and in the second pass it executes the statements in order. The crucial thing is that `var` statements are effectively split into two: in the first pass, a property with value `undefined` is added to the Variable object if it doesn't already contain a property with that name (continued...)
Tim Down
(continued) ... and if there is a initial value for the variable specified in the variable declaration, it isn't assigned to the Variable object until the second pass. Any function declaration, however, creates a property of the Variable object in the first pass with that function as the value. So in your example, the order of events is: 1) `var say = function() { alert("speak"); }` creates a property `say` of the Variable object with value `undefined`; 2) `function say() { alert("say"); }` overwrites the `say` property of the Variable object with that function; (continued...)
Tim Down
(continued) ... 3) `var say = function() { alert("speak"); }` overwrites the `say` property of the Variable object with the "speak" function; 4) `window.say()` is called. `window` is effectively the same object as the Variable object, so it calls the `say` property of the Variable object, which is the "speak" function. 5) Repeat of 4).
Tim Down
I really recommend reading the ECMAScript specification on this subject to get a full idea of what's going on.
Tim Down
One final thing: this answer isn't entirely correct. The behaviour has nothing to do with either declaration being in a "more specific scope": the scope is the same for all the declarations.
Tim Down
With all due respect, all this answer does is summarize the question. Can you cite the spec where this is explained? +1 to Tim Down's comments.
gWiz
+1  A: 
say();

function say()
{
    alert("say");
}

Here the interpreter fetches the definition of say() when it is called, and executes it.

say();

say = function()
{
    alert("say");
}

Here there is no definition of say() to fetch - instead you are assigning an anonymous function to a variable. The interpreter can't "find" this like it can find forward declarations.

function say()
{
    alert("speak");
}

say();

function say()
{
    alert("say");
}

Here say is defined and then redefined - the last definition wins out.

say = function()
{
    alert("speak");
}

say();

function say()
{
    alert("say");
}

say();

Here say is a variable pointing to an anonymous function (after the first statement is interpreted). This takes precedence over any function definition, the same as if you had placed the function definition before the assignment.

But if you had

say();

say = function()
{
    alert("speak");
}

say();

function say()
{
    alert("say");
}

Then you would get "say" followed by "speak".

danben
A: 

Its always good idea calling the function later, even though javascript works like that.

Most of the languages won't work that way, instead do this.

function say(){
    alert("say");
}

say();

or

say = function(){
    alert("say");
}

say();

or

(function(){
    alert("say");
})();
S.Mark
+4  A: 

The following excellent article by kangax answers all your questions and more: http://yura.thinkweb2.com/named-function-expressions/

Tim Down
+1 That really is an excellent article. I was needing info on named function expressions yesterday, and came across another question or two where you referenced it in a comment. Answered nearly every question I had.
patrick dw
kangax has written some other excellent articles, and he's sharp and thorough in his research. Seems rather a waste of his time working on Prototype.
Tim Down