Hoisting, scope and function declaration

95 views Asked by At

Here are the simple code:

{
  function a() {}
  a = "test";
}

console.log(a);

I expected to get "test" but got "ƒ a() {}" in console. Why is that? Can anyone explain this behaviour?

2

There are 2 answers

0
Alexander Nenashev On

Our code works in the non-strict mode so some quirk things happen (it's legacy).

Please use the strict mode only, which throws an error and prohibits such strange effects that lead to bugs.

{
  /* in the non-strict mode a function is declared in a block
     (creating a local variable)
     but also hoisted to the global scope 
  */
  function a() {} 
  a = "test"; // this assigns to the local variable, not affecting the hoisted one
}

console.log(a); // prints the hoisted variable

The strict mode:

'use strict';
{
  function a() {} 
  a = "test"; 
}

console.log(a);

0
traktor On

Sloppy mode vs Strict Mode

It has to do with strict and quirks mode in modern browsers, and that the effect of defining a function (which used to be always hoisted to the scope of the the function it's in) inside a block statement.

  1. In modern browsers in strict mode, the function is defined with block scope only, and the value of a is modified by the assignment. Outside the block a remains undefined.

"use strict";
{
  function a() {}
  a = "test";
  console.log("Inside strict mode block after assignment:")
  console.log(a);
}

console.log("Outside block a is undefined");
console.log(a);

  1. In quirks mode however (without "use strict"), browsers seem to hoist the function definition to function scope, but still keep a binding for a inside the block. The practical effect is that the hoisted function definition remains in place outside the block, while inside the block, the local value of a is updated by the assignment.

{
  function a() {}
  a = "test";
  console.log("Inside block after assignment:")
  console.log(a);
}

console.log("Outside block a is the hoisted function");
console.log(a);


I believe the behavior in quirks mode was arrived at to maintain some similarity with browser behavior prior to the introduction possibly block scoping variables - _if you don't overwrite the function name with

Bottom line? _The handling of defining functions within the block of a conditional or loop statement (using { and } delimiters) prior to ES5 was not standardized. Many people would not have done it for stylistic reasons - why define a function name that will be hoisted inside a block. Which left the door open to not hoist function names outside the block in strict mode.

Moral of the story - don't write serious code in sloppy mode :-)