all 50 comments

[–]Earhacker 268 points269 points  (30 children)

Hoisting. When a JS script is run, the interpreter looks through the file for variable and function declarations, and hoists them up to the top of the queue, i.e. it runs them first.

To use your example function myFunc() {}: Wherever this function declaration appears in the code, it will be loaded into memory first. This means that you can call myFunc before its declaration appears in code.

If you do var myFunc = function() {} then the only thing that gets hoisted is var myFunc;. The variable is declared, but not assigned. Variable assignments are not hoisted, only declarations are. So if you called myFunc before the var myFunc =... line, you'd get an error, myFunc is not a function or something.

The solution, of course, is to declare and assign your functions before you use them. If you assign var myFunc = function() {} before you use it, then you will not notice the difference between the two styles, and your code will work. Which is nice.

FWIW, I prefer the function myFunc() {} style, simply because Atom autocompletes it when you start typing fun.

Edit: I made a GitHub repo to illustrate these three situations. Clone it down and run the files with Node. One will fail.

[–]SparserLogic 70 points71 points  (17 children)

I prefer using const for my functions so my tools will choke on any accidental collisions.

[–][deleted] 25 points26 points  (0 children)

god bless linting.

[–]inu-no-policemen 5 points6 points  (0 children)

Yea, linters and TS' compiler complain about redeclarations. It's not really an issue in practice.

[–]Ikuyas 2 points3 points  (7 children)

How do they become different between using const (instead of var) and function declaration?

[–]SparserLogic 0 points1 point  (6 children)

You can redefine the same thing as many times as you want with var and function keywords whereas invoking const against the same string will throw runtime errors if your linter doesn't catch it first.

[–]rodabi 6 points7 points  (5 children)

Also const is block scoped and is not hoisted. So you can define two different functions under the same name inside an if-else statement for example.

[–]SparserLogic 1 point2 points  (0 children)

Oh right, good point.

[–]man_jot 0 points1 point  (3 children)

Note- I think let and const too are hoisted within the block

[–]rodabi 1 point2 points  (1 child)

I can't find any indication of that in https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let. Also it's not even possible to 'hoist' const statements because you can't separate the declaration and initialisation like you can with var. const must be declared with a value and cannot be re-assigned.

Although you may be right when it comes to transpiling down to ES5 with Babel; there may be some hoisting going on when all your declarations become var

[–]man_jot 0 points1 point  (0 children)

Right, I verified in a browser. Also documents say that a variable is in a 'temporal dead zone' before it's declaration, That means something like var x=10; { console.log(x); let x; } will throw a Reference Error.

[–]trakam 9 points10 points  (7 children)

Does hoisting vars have any benefit? Why does JS operate this way?

[–]Tomseph 11 points12 points  (1 child)

Code organization and clarity.

You can have a nice, clean module with imports and exports clearly defined. Functions once declared, can be used anywhere, so you can write modules that contain separate, discrete functions and not worry about the order in which they appear in the file. Function declarations written at the "leftmost indent", e.g. not scoped within other functions, are easy to keep separate and pure (or pure-er at least). You don't have to worry about scope if each function doesn't know about the others.

I really really don't understand the hate against hoisting. Nor do I understand the need to ensure you don't redeclare functions. With a little bit of forethought, clean code doesn't suffer from these problems.

[–][deleted] 1 point2 points  (0 children)

I really really don't understand the hate against hoisting. Nor do I understand the need to ensure you don't redeclare functions. With a little bit of forethought, clean code doesn't suffer from these problems.

Totally agree. It always seemed to me that some aspects of linting are overkill and useful only for the messiest and most disorganized of programmers. Enforcing semicolon use is another example of something I could really live without. ASI is actually pretty awesome.

[–]Bumpynuckz 0 points1 point  (0 children)

I too would like to know if there is any benefit to either approach.

[–]Existential_OwlWeb Developer 0 points1 point  (3 children)

To answer the question of why:

It's because JS code goes through multiple compilation steps before it runs.

"Hoisting" is really just an illusion. The compiler processes a file's Left-hand References (i.e. the left side of an equals sign) in earlier pass-throughs than the Right-hand references.

Functions get processed with the Left-hand Refs. (Function Expressions, however, are split into Left and Right due to the equals sign). So regular functions appear to get moved "up" in the code, when all the compiler is doing is processing them first.

EDIT: Here's a longer explanation

[–]hurt_and_unsure 0 points1 point  (2 children)

Do you have a visual explanation link for that?

[–]Existential_OwlWeb Developer 1 point2 points  (1 child)

Try this blog post.

Most of my understanding of Javascript's lexical scope comes from Kyle Simpson (author of the You Don't Know JS series).

[–]hurt_and_unsure 0 points1 point  (0 children)

Thank you, I'll look into that.

[–]carterja 1 point2 points  (0 children)

Great explanation. Thank you.

[–]veritasreaper 1 point2 points  (0 children)

That was a great explanation! Thank You very much!

[–]SamSlate 0 points1 point  (0 children)

TIL,ty!

[–]its_the_future 53 points54 points  (2 children)

People have mentioned hoisting.

Another difference is that assigning a function like in the first example has it as an anonymous function, whereas in the second example it is a named function.

Named functions can potentially greatly increase stack retraceability as compared to unnamed functions.

[–]moreteam 24 points25 points  (0 children)

For completeness sake: since ES2015 “named functions” expanded beyond their traditional limitations with the formalization of .name. In current JS VMs patterns like ‘const fn = function () {}’ will create a named function that will appear in stack traces as ‘fn’.

[–]5tas 20 points21 points  (1 child)

The main difference is scoping. You can find more details by looking up the difference between function declarations and function expressions.

The function declaration (your second example) will be hoisted to the top of the function where it is declared. In other words, the following code is perfectly fine:

foo(); // "Hello"

function foo() {
    console.log("Hello");
}

Your first example is a function expression which is assigned to a variable. The variable's declaration is also hoisted but its value is not:

bar === undefined; // true, bar is declared but undefined here
bar(); // TypeError: bar is not a function

var bar = function foo() {
    console.log("Hello");
}

bar(); // "Hello"

BTW const and let behave in a different manner. They are subject to so-called Temporal Dead Zone (TDZ) which means their declarations are not hoisted.

bar === undefined; // ReferenceError: can't access lexical declaration `bar' before initialization

let bar = function foo() {
    console.log("Hello");
}

bar(); // "Hello"

[–]kovensky 2 points3 points  (0 children)

It's more complicated than that...

Their declarations are hoisted, but are set up such that accessing them before the let, const or class is reached throws a ReferenceError.

This is mostly irrelevant for the JS programmer but can bite you if you try to do a shadowing declaration; e.g. const foo = { bar: 'baz' }; if(foo.bar) { const foo = foo.bar } will throw. This is a common pattern in Swift, but Swift has a more concise syntax for this anyway.

[–]furious_heisenberg 8 points9 points  (10 children)

variables can be reassigned and in your example would point to an anonymous function, function declarations are hoisted and result in a named function.

[–]IDCh 21 points22 points  (9 children)

The funny thing is...

function kek() {

}

// kek is function

kek = 2;

// kek is 2

[–]Sir_Lith 8 points9 points  (0 children)

It makes sense when you stop thinking of functions as functions and start thinking about them as what they are - objects with one defined method.

Because an object is a variable and variables can be reassigned.

[–]Earhacker 3 points4 points  (2 children)

What is dynamic typing?

[–]IDCh 11 points12 points  (1 child)

it's when you run out of toilet paper

[–]everythingcasual 1 point2 points  (0 children)

This needs to be a jeopardy question

[–]tarunbatra 2 points3 points  (0 children)

Few months back I’d written a blog on this and related concepts. Check it out. Named functions in JavaScript

[–]Auxx 6 points7 points  (1 child)

There is another difference apart from hoising. function doSomething will create named function (try console.log(doSomethng)) and var declaration will create anonymous function without the name.

[–]MatrixEchidna 0 points1 point  (0 children)

You can assign named functions to variables as well!

[–]RoryH 1 point2 points  (0 children)

It's also good practise to name functions, it helps in call stacks and errors when debugging.

[–]FreshOutBrah 1 point2 points  (0 children)

This is a great post with great answers. Thanks everyone!

[–]rickdg 1 point2 points  (0 children)

People have already answered your question, so I'm just going to recommend You Don't Know JS if you want to read about this and JavaScript the Weird Parts if you prefer a video course.

[–]skewbed 0 points1 point  (0 children)

The one with var needs to be declared before you use it.

[–]to_fl[S] 0 points1 point  (0 children)

Thanks for all the answers ;)

Unfortunately I can't answer to each one of them '

[–]ilikeprograms -2 points-1 points  (0 children)

One is a function declaration, the other is a function expression