3

Can anybody tell me how it work by step, and why is different result:

  1. Why is there no SyntaxError in the first case:

first:

var a = 'foo'; 
function a() { };
console.log(a); // foo

second:

// Uncaught SyntaxError: Identifier 'a' has already been declared
{
    var a = 'foo'; 
    function a() { };
}
console.log(a);
  1. How the function a() {} overwrites var a?

// **only no use strict!!!**
var a = 'foo';
console.log(a); // foo
{
    console.log(a); // function a() {} - i guess that is the result of hoisting inside  the block but on this step  the global variable a is still 'foo'
    function a() {} // on this step -  the global variable a = function a() {}. How to explain that? 
    a = "bar"; //  on this step - overwrites only the local variable a 
    console.log(a); // bar
}
console.log(a); // function a() {}
and if i write so:

// **only no use strict!!!**
var a = 'foo';
console.log(a); // foo
{
    console.log(a); // function a() {}
    a = "bar";  
    function a() {} //  on this step -  the global variable a = bar. Why is not function a() {} ?
    console.log(a); // bar
}
console.log(a); // bar
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
apl-by
  • 98
  • 6
  • 1
    See: [confused about function declaration in { }](https://stackoverflow.com/q/58619924) and [What are the precise semantics of block-level functions in ES6?](https://stackoverflow.com/q/31419897) – Nick Parsons Aug 21 '21 at 08:48

2 Answers2

8

TL;DR Don't do that. The semantics for it are complicated and (as you noted) they're different in strict mode vs. loose mode.


  1. Why is there no SyntaxError in the first case

Because that's just how JavaScript's var and function declarations were designed back in 1995. Both create a "binding" (effectively a variable) in the function or global scope where they appear. If you use the same name for both, that same binding is reused. The function is assigned to it first (because function declarations are hoisted), but then when the step-by-step execution of the code occurs the a = 'foo' part of var a = 'foo' runs and overwrites the binding's value with 'foo'.

second

Because you're declaring that function in a block, and the semantics are that are much newer (ES2015); prior to that, every JavaScript engine was free to handle that however it liked (and they handled them differently, and even the same engine would change how it handled them from one version to another).

The semantics there are that a let-style binding is created at the top of the block and the function is assigned to it. Even though var doesn't have block scope, you're not allowed to declare a var variable and a let variable with the same name in a block:

{
    // Not allowed
    let a = 1;
    var a = 2;
}

When let-style declarations were added, this was disallowed, even though (again) the var isn't contained to the block.

With that in mind, here's roughly how the JavaScript engine interprets your "second" code:

// Uncaught SyntaxError: Identifier 'a' has already been declared
{
    let a = function a() { };
    var a = 'foo'; 
}
console.log(a);

Since that's the same situation (a var-style declaration and a let-style declaration in the same block), you're just not allowed to do that.

How the function a() {} overwrites var a?

It's that implicit local let declaration again. Here's roughly how the JavaScript engine interprets that function declaration:

// **only no use strict!!!**
var a = 'foo';
console.log(a); // foo
{
    // Block-local binding and function creation are hoisted
    let a = function a() { };
    console.log(a); // function a() {}
    a<outer> = a<local>; // <======== Where the declaration was (not real syntax)
    a = "bar"; // only the local is set
    console.log(a); // bar
}
console.log(a); // function a() {}

Note that weird bit where the function declaration originally was:

a<outer> = a<local>; // Where the declaration was

The location of the declaration in the block controls when the value is assigned to the outer var. Yes, this is weird. So the declaration and function creation are hoisted, and then assignment to the outer variable is done where the declaration appeared in the code (even though normally, the location of a function declaration relative to step-by-step code is irrelevant). Importantly, that assignment is from the local's current value as of where that code is reached in the step-by-step execution.

on this step - the global variable a = bar. Why is not function a() {}

Because the assignment is before the declaration in the block, and the local a is copied to the outer a where the declaration was. Here's roughly how the JavaScript engine handles that:

// **only no use strict!!!**
var a = 'foo';
console.log(a); // foo
{
    let a = function a() { }; // Hoisted
    console.log(a);
    a = "bar";
    a<outer> = a<local>; // <======== Where the declaration was (not real syntax)
    console.log(a); // bar
}
console.log(a); // bar

Again, though, just don't do that. It's really complicated and arcane. I wrote an entire section on it for my recent book JavaScript: The New Toys (Chapter 3), and yet to answer your question I had to go back to that chapter and remind myself how this worked. Save those brain cells for something important.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
-1

1st Case Works because you can make Empty Functions in JavaScript

2nd Case causes error because of the new ES6

This is an EcmaScript 6 change. From ES6 onwards it's no longer allowed to have duplicate bindings within a block scope. Read more

3rd Case I don't know

Aditya
  • 1,132
  • 1
  • 9
  • 28