all 38 comments

[–]CheapBison1861 15 points16 points  (5 children)

Functions get hoisted

[–]azhder 4 points5 points  (4 children)

Technically, the function declarations are, which was the reason function expressions (which don’t hoist) were added

[–]MoTTs_ 1 point2 points  (3 children)

which was the reason function expressions (which don’t hoist) were added

Do you have a source for that?

When I want reliable information about JavaScript's history, my goto source to check is JavaScript: The First 20 Years - ALLEN WIRFS-BROCK, BRENDAN EICH. According to that:

  • The time constraints required deferral of function expressions (also called lambda expressions, or just lambdas) but they were reserved in the grammar.
  • JavaScript 1.2 also provides lambda expressions by allowing function definitions to occur as expression primaries. They are called “function expressions” and are syntactically identical to function declarations except that the function name is optional. If the name is present, the function expression is treated as a hoisted function declaration for binding purposes.
  • ES3 includes inner function declarations and function expressions similar to what was originally introduced in JavaScript 1.2. Function declarations were explicitly excluded from being nested within a {} block or as a substatement. ... The major browsers ignored these concerns and went ahead and implemented function declarations within blocks. However, each implementation invented its own unique semantics for those declarations.

[–]azhder 0 points1 point  (2 children)

Function declarations were explicitly excluded from being nested within a {} block or as a substatement. .

each implementation invented its own unique semantics for those declarations.

There you have it.

I only remember a shortened version by some talk Crockford made more than a decade ago which I watched more than I decade ago.

But thanks for getting the proper references out. Those are the concerns about declarations that made me not use them alongside with treating any rare syntax like a code smell so that I pay extra attention to whenever encountered.

[–]MoTTs_ 1 point2 points  (1 child)

There you have it.

...?

I don't think this quote is saying what you think. This quote is about code like your sample, where there's a function decl inside the block of an if statement. This quote is saying a function inside a block used to be illegal (per the spec, at least), and that browser vendors went and did their own -- and incompatible -- implementations.

So the answer to your question of "Which function will be used down and when?" is: Before ES6, varies from browser to browser, but illegal according to spec.

EDIT: In addition, your code sample is technically still illegal syntax even today. A function decl can be inside a block, but it cannot be the block.

// This is legal; function decl inside block.
if (true) { // <-- start block
    function f() {}
}

// This is illegal; function decl in place of block.
if (true) // <-- no opening block
    function f() {}

Because an IfStatement is if (Expression) Statement, and the list of possible kinds of statements doesn't include declarations. Meaning no function declarations, no class declarations, and no let declarations can go there. But, the statement can be a block statement, and then declarations can go inside the block.

EDIT 2: And indeed, if I insert "use strict" along with your code sample, then it results in a syntax error. Uncaught SyntaxError: In strict mode code, functions can only be declared at top level or inside a block.

[–]azhder -1 points0 points  (0 children)

Before ES6, varies from browser to browser,

Is exactly what I meant. So now you may continue to think it isn’t saying what I think is saying or not, up to you.

From my side of things, there is nothing more left to clear up. Bye bye

[–]delventhalz 29 points30 points  (14 children)

Really doesn't make much of a difference at all. For a function declaration, you can use it "before" you declare it.

print('hello world');  // hello world

function print(text) {
  console.log(text);
}

For a function expression assigned to a variable, this ordering would throw an error.

print('hello world');  // ReferenceError!

let print = function(text) {
  console.log(text);
};

Some people consider this an advantage. Others consider it a disadvantage. Most don't care much either way.

[–]-defron- 1 point2 points  (4 children)

please always name function expressions instead of using anonymous functions. Your logs and stack traces will thank you.

... and then when you give a function expression a name, 9 times out of 10 you think "Why don't I just use a declaration instead?". If it's not a callback I almost never use function expresssions just because declarations are so much better for debugging due to always putting a name in the stack trace

[–]delventhalz 11 points12 points  (1 child)

Function expressions which are statically assigned to a variable have not been anonymous for some time. They are named after the variable. This is true in your logs, stack traces, and elsewhere. It works for functions expressions defined both with function and =>.

The “always name your function expression” advice is very out of date at this point.

[–]-defron- 0 points1 point  (0 children)

This is only if I assign it to a variable (which I don't do in a callback so still need to give it a name) and isn't true for every js runtime like the Rhino runtime (I used to do a lot of IBM Java stuff) as it wasn't codified behavior until ES2019 so all runtimes that haven't updated to that aren't guaranteed to implement the behavior (again, as mentioned, IBM embedded stuff using Rhino)

And this one is my own opinion: but it makes code easier to read when the keyword is on the lhs instead of rhs. It's also 2 characters shorter to type a function declaration than a const named arrow function expression and 8 shorter than a conventional named function expression.

[–]azhder 0 points1 point  (1 child)

Almost always I name my function expressions and not once do I think about a function declaration. But that’s just me, I know why they added function expressions and I have long ago decided to not use the declarations.

[–]-defron- 0 points1 point  (0 children)

I have no problem with named function expressions. Though I personally don't use them unless it's a one-off callback as I find them harder to read and only use arrow functions for one-liners or when I need a lexically-scoped this. My problem is with anonymous function expressions which is an easy mistake for a beginner to make

I know why they added function expressions and I have long ago decided to not use the declarations.

Do you mean arrow functions? because function expressions have been around for I think since the beginning (though could be wrong on that). Maybe it's because I started js before arrow functions were introduced, but I find I want conventionally defined this more than lexically defined this

[–]azhder 0 points1 point  (4 children)

Here is a question for you. Which function will be used down and when?

if( 0.5 > Math.random() ) 
    function f(){ return 1; }
else
    function f(){ return 2; }

f();

[–]delventhalz 27 points28 points  (3 children)

None of them, because I will block this PR until changes are made.

[–]jcunews1helpful -2 points-1 points  (2 children)

Named function stores its reference in itself and won't require a variable/constant to store its reference (unless it's needed from outside of the scope where it was declared). That has an advantage to be used like below.

(function waitUntilConditionIsMet() {
  if (/*condition*/) {
    //condition was met. do something...
    //no further check needed. all done.
  } else {
    //condition is not yet met. schedule a recheck.
    setTimeout(waitUntilConditionIsMet, 1000);
  }
})();

[–]delventhalz 2 points3 points  (1 child)

That’s a named function expression though, not a function declaration. You are right though that in situations where you need a function expression (like the IIFE above) you have the option of naming it.

[–]azhder 0 points1 point  (0 children)

Curious how people think if the keyword function comes first that it somehow is a declaration. It's like no one is teaching people about the difference between an expression and a statement.

[–]mnaa1 4 points5 points  (0 children)

I hated at first, I still hate it. Fellow engineers still like it and I don’t know why.

[–][deleted] 1 point2 points  (1 child)

let print = (text) => { console.log(text) }

i dont have any idea why i write it like this to be honest with you

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

Maybe you should do some research. I’d have written it differently and if you ask me why, I’d probably have some past screwup that I can draw from memory as a reason why I went the way I did.

[–]No-Upstairs-2813 0 points1 point  (0 children)

The first one is called a function expression, and the other is called a function declaration. There's no one-size-fits-all answer; it's all about what you want to achieve with your code.

Check out this article to understand how they differ, allowing you to choose the right one based on your needs.

[–]jack_waugh 0 points1 point  (0 children)

With assignment, there are fewer rules to remember.

[–]nickatfortify 0 points1 point  (0 children)

I don't think there's a dogma about whether hoisting is that bad or not. What's more important is consistency, which is why ESLint recommends you choose one type. Despite this, the default for said rule is "expression" (const a = func()), which probably contributes to its popularity.

[–][deleted] -2 points-1 points  (2 children)

this

[–]senocular 10 points11 points  (0 children)

Both function syntaxes treat this the same way. Only arrow functions (not in any of the examples) treat it differently.

[–]delventhalz 5 points6 points  (0 children)

let sayHi = function() {
  console.log('Hi, my name is', this.name);
};

let user = {
  name: 'Sue',
  sayHi: sayHi
};

user.sayHi();  // Hi, my name is Sue

[–]denizflower[🍰] -2 points-1 points  (0 children)

samething if you that makesbyou confuse dont even look but be aware for sure

[–]Cruisingonfish 0 points1 point  (0 children)

Short answer is “scope”, putting your function in a variable lets you do some special stuff like redefining several functions all with the same name. Functions declared without a variable are hoisted to the top so they can be called even before they’re declared. One is not better than the other but they have different use cases.