98

I wrote the following script just to see what happens when a variable and a function that has a function assigned to it have their names clash:

var f = function() {
    console.log("Me original.");
}

function f() {
    console.log("Me duplicate.");
}

f();

The output I'm getting is "Me original." Why was the other function not called?

Also, if I change my original assignment to var f = new function() {, I get "Me original", followed by a TypeError saying object is not a function. Can someone please explain?

abbotto
  • 4,259
  • 2
  • 21
  • 20
ankush981
  • 5,159
  • 8
  • 51
  • 96
  • Both functions have the same name "f" and I'm sure JS gets confused with the multiple definitions. – Dean.DePue May 27 '14 at 12:23
  • 26
    @Dean.DePue — There's no confusion on JavaScript's part. The rules for handling them are quite clear (and explained by Benjamin in his answer). – Quentin May 27 '14 at 12:28
  • 4
    Curiosity, still the best way to learn about a language. :-D – Cerbrus May 27 '14 at 12:28
  • 2
    Also, I imagine it's pretty impossible for something as immaterial as "JavaScript" to "feel" confused (or any emotion, for that matter) ;-) – Cerbrus May 27 '14 at 12:30
  • 2
    Why should hoisting reverse the order in the second example? – Cerbrus May 27 '14 at 12:48
  • @dystroy That helped a lot! So I think the `var f = ...` line was not a declaration and therefore appeared below. – ankush981 May 27 '14 at 13:01
  • 1
    Note that any time you have `...new function() {...`, you're probably looking at a singleton constructor definition-and-execution. The `new` operator in JS consumes your function and gives back an object. Your function is then inaccessible (except that your new object might have a reference to its constructor). There are some answers already on the site you should be able to find to explain the details. – Keen May 27 '14 at 17:49
  • 5
    Steps for growing in knowledge of javascript: 1) Use 'use strict' 2) Always use either jslint or jshint 3) Look up the things that jslint or jshint complains about 4) Rinse and repeat – steve-er-rino May 30 '14 at 17:05
  • Why do we still use this language. – Millie Smith Jun 07 '14 at 07:22
  • @MillieSmith No alternative. jQuery, etc., are neat, but needs to know JavaScript to do anything serious. – ankush981 Jun 07 '14 at 16:57
  • I just think we should have a better language by now @dotslash – Millie Smith Jun 07 '14 at 16:58
  • @MillieSmith Agree. I'd love to see something like Google Dart pick up momentum, but who are we to decide? :( – ankush981 Jun 07 '14 at 17:25

3 Answers3

171

Function declarations are hoisted (moved to the top) in JavaScript. While incorrect in terms of parsing order, the code you have is semantically the same as the following since function declarations are hoisted:

function f() {
    console.log("Me duplicate.");
}
var f = function() {
    console.log("Me original.");
}


f();

Which in turn, with the exception of the function's name is the same as:

var f = function() {
    console.log("Me duplicate.");
}
var f = function() {
    console.log("Me original.");
}


f();

Which in turn, because of variable hoisting is the same as:

var f;
f = function() {
    console.log("Me duplicate.");
}
f = function() {
    console.log("Me original.");
}

f();

Which explains what you're getting, you're overriding the function. More generally, multiple var declarations are allowed in JavaScript - var x = 3; var x = 5 is perfectly legal. In the new ECMAScript 6 standard, let statements forbid this.

This article by @kangax does a fantastic job in demystifying functions in javascript

Wilfred Hughes
  • 29,846
  • 15
  • 139
  • 192
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • 2
    Can you really simplify `function f()` to `var f = function()` that much? Are hoisting and function names really the *only* difference? – djechlin May 27 '14 at 12:28
  • 6
    @djechlin in the context of this question - yes. Generally, it's more subtle - see http://stackoverflow.com/questions/336859/var-functionname-function-vs-function-functionname . From the compiler perspective, they're different - but from the programmer perspective - we're _close enough_ to it to claim that. That's why I added that long "while incorrect in terms of parsing order, the code you have is semantically the same as" instead of saying "is the same as". Good point. – Benjamin Gruenbaum May 27 '14 at 12:30
  • @djechlin In case you want the 'hardcore version', if you're familiar with BNF notation I _really_ like the way it's explained in http://es5.github.io/#x13 , although I guess that requires knowing the surrounding terminology. – Benjamin Gruenbaum May 27 '14 at 12:32
  • @BenjaminGruenbaum Thanks, but I still think I'm confused about hoisting. Please see the addition I did to my question. Thank you very much! – ankush981 May 27 '14 at 12:48
  • 1
    @dotslash please do not edit your original question and change it, that's considered bad mannered here - also, mixing several questions in one is also considered bad mannered here. You can ask a new question instead or, if you think it's too minor, ask for clarification in the comments (that's what they're for anyway). In the above code, _both_ versions of `f` get hoisted, and the `"Me Original"` version just gets hoisted _later_, they each get moved to the top but in the same order. I'd just like to add that generally, you should not name several functions the same way :) – Benjamin Gruenbaum May 27 '14 at 12:50
  • @BenjaminGruenbaum Sorry about the changes, but I didn't know the protocol. I had seen people add more info to their questions and I didn't think the comment would be properly formatted; that's why I added to the original question. Anyway . . . So I take it that hoisting order in such cases is not well defined? Thanks in advance! :) – ankush981 May 27 '14 at 12:56
  • @dotslash hoisting order in such cases is well defined (in a first come first serve fashion, as indicated in [the specification](http://es5.github.io/#x13) but I would definitely still not rely on it or use it. This are all things that code analysis tools like ternjs or jshint will rightfully complain about and should not make it into production :) – Benjamin Gruenbaum May 27 '14 at 13:00
  • 1
    @BenjaminGruenbaum Okay, this is clear now. Many thanks for spending time on this! :D – ankush981 May 27 '14 at 13:02
  • 5
    In strict mode you can't `var` the same name twice in the same scope. – Hoffmann May 27 '14 at 16:05
  • Good Point @Hoffmann I'm on mobile so I can't really make edits, feel free to edit that in. – Benjamin Gruenbaum May 27 '14 at 16:59
  • Code 1 to code 2 is an oversimplification. Code 2 to code 3 seems irrelevant. -1. – bjb568 May 31 '14 at 00:50
  • @false `"function f()" !== "var f = function()"`, "Which in turn, because of variable hoisting is the same as:" is irrelevant. – bjb568 May 31 '14 at 01:11
  • 1
    @bjb568 actually, it is a crucial part of the answer, and explains how multiple declarations of the same variables are parsed. I did start this answer with a disclaimer as well as linked to the relevant part of the specification in the answer. – Benjamin Gruenbaum May 31 '14 at 01:13
  • @Benjamin Uh… ok. That should be obvious (i.e. prerequisite to understanding the rest of the answer and about any js answer on SO). And why the lack of semicolons? – bjb568 May 31 '14 at 01:14
  • 4
    "That should be obvious" - to _you_ perhaps, but it was not obvious to me at a point, and it was not obvious to OP when he asked it, and naming, and more generally how the lexical environment is managed in JavaScript was one of the hardest things to grasp when first learning JavaScript to me. I would not be so quick to insult people who do not understand it. – Benjamin Gruenbaum May 31 '14 at 01:18
  • @BenjaminGruenbaum @Hoffmann it's not the case that duplicate `var` statements are forbidden in strict mode. `(function() { 'use strict'; var x = 1; var x = 2; console.log('foo'); })()` works fine. I've edited the answer. – Wilfred Hughes Sep 06 '16 at 19:52
  • hoisted does not mean move to the top, it only means that the engine when it parses your code, sets up memory for the functions and variables, in case of functions whole thing is put in the memory, and in case of variables only memory is reserved and javascript puts the value undefined till in execution phase var something = something is hit. Am i right? – Suraj Jain Jan 25 '18 at 05:22
10

If doesn't look like anyone answered your follow-up question so I'll answer it here, though you should generally ask follow-up questions as separate questions.

You asked why this:

var f = new function() {
    console.log("Me original.");
}

function f() {
    console.log("Me duplicate.");
}

f();

prints out "Me original." and then an error.

What is happening here is that the new causes the function to be used as a constructor. So this is equivalent to the following:

function myConstructor() {
    console.log("Me original.");
}
var f = new myConstructor();

function f() {
    console.log("Me duplicate.");
}

f();

And thanks to the function hoisting that Benjamin explained, the above is essentially equivalent to this:

var myConstructor = function() {
    console.log("Me original.");
};
var f = function() {
    console.log("Me duplicate.");
};

f = new myConstructor();

f();

This expression:

var f = new function() {
    console.log("Me original.");
}

causes a new object to be constructed and assigned to f, using an anonymous function as the constructor. "Me original." is printed out as the constructor executes. But the object that is constructed is not itself a function, so when this eventually executes:

f();

you get an error, because f is not a function.

JLRishe
  • 99,490
  • 19
  • 131
  • 169
2

Forgive me if this is the wrong way to approach adding a point. I haven't been around here all the much, and would welcome constructive direction and/or criticism.

Benjamin's answer addresses the OP's question excellently, but I'd like to add one tweak that'll give us a full tour of hoisting and its oddities.

If we begin the original code with a call to f, like so:

f();

var f = function() {
   console.log("Me original.");
};

function f() {
   console.log("Me duplicate.");
}

f();

The output will then be:

Me duplicate.
Me original.

The reason being that var and function statements are hoisted in slightly different ways.

For var the declaration is moved to the top of the current scope*, but any assignment is not hoisted. As far as the value of the declared var goes, it's undefined until the original assignment line is reached.

For functionstatements, both the declaration and definition are hoisted. Function expressions, as used in the var f = function() {... construct, are not hoisted.

So after hoisting, execution is as if the code were:

var f; // declares var f, but does not assign it.

// name and define function f, shadowing the variable
function f() { 
  console.log("Me duplicate.");
}

// call the currently defined function f
f(); 

// assigns the result of a function expression to the var f,
// which shadows the hoisted function definition once past this point lexically
f = function() { 
  console.log("Me original."); 
}

// calls the function referenced by the var f
f();

*All JavaScript scope is lexical, or function, scope, but it seemed like it would just confuse things to use the f word at that point.

codelahoma
  • 895
  • 8
  • 14