3

I was reading the new features in ECMA6. One of the new features is Block Scoped Functions . That means we can have a same name for functions in different blocks. As in the code below block1 foo() logs 1 then similarly block2 foo() logs 2 and block0 foo() or global scope logs 4. And that is the expected result. But what i can't understand is that Why does the last console.log(foo()) logs 1 and not 4 because it is under block0 or global scope instead it logs 1 which is block1 foo().

//block0
function foo () { return 4 }
console.log(foo());//will log 4
{
  //block1
    function foo () { return 1 }
    console.log(foo());//will log 1
    {
    //block2
        function foo () { return 2 }//a block level function
        console.log(foo());//will log 2
    }
    console.log(foo());//will again log 1
}
console.log(foo());//should log 4 but logs 1 why?

Now again if I enclose the above code into a another block it works as expected. This is a bit confusing to me. What actually is causing this behviour?

{//block0
function foo () { return 4 }
console.log(foo());//will log 4
{
  //block1
    function foo () { return 1 }
    console.log(foo());//will log 1
    {
    //block2
        function foo () { return 2 }
        console.log(foo());//will log 2
    }
    console.log(foo());//will again log 1
}
console.log(foo());//will log 4 but 
}
Manish
  • 4,692
  • 3
  • 29
  • 41
  • Are you familiar with [What are the precise semantics of block-level functions in ES6?](https://stackoverflow.com/q/31419897/1048572) already? – Bergi Jun 11 '17 at 22:56
  • Is there any difference between block0 being a function or global scope? Also, in what browser, in what environment (console?) are you testing this? – Bergi Jun 11 '17 at 22:58
  • @Bergi i have tested it in chrome and IE11. And block0 over here iis global scope. – Manish Jun 12 '17 at 03:45

3 Answers3

2

You need to ensure strict mode is enabled. Since it is enabled in modules by default, I guess your sample code doesn't show that. Kangax's compatibility table shows an appropriate test.

"use strict";
//block0
function foo () { return 4 }
console.log(foo());//will log 4
{
  //block1
    function foo () { return 1 }
    console.log(foo());//will log 1
    {
    //block2
        function foo () { return 2 }//a block level function
        console.log(foo());//will log 2
    }
    console.log(foo());//will again log 1
}
console.log(foo());//logs 4
CodingIntrigue
  • 75,930
  • 30
  • 170
  • 176
1

Fully amended answer for in-depth explanation

First things first: the problem appears because you are not using use strict mode. What is the difference in strict and non-strict mode in this case? The difference is the way simple block scoping is treated.

In both modes a brackets block creates a scope. Declarations inside that scope can be viewed inside the scope itself and inside further enclosed scopes.

{ // level 1 scope
  function asdf() {alert("asdf");}
  { // level 2 scope
    asdf(); // Yay I can use it!
  }
}

However, in ES5 and in ES6 non strict, a simple block scope is not treated as a true enclosing scope. Declarations inside a simple block scope get hoisted all the way up to the first true enclosing scope they can find. In those cases, a true enclosing scope is either a function's scope or the global scope.

The above example, in non strict mode is equivalent to this:

var asdf0 = undefined; // Haha! The declaration has been hoisted up to the global scope!
{ // level 1 scope
  function asdf1() {alert("asdf");}
  asdf0 = asdf1; // The definition is ready and will be assigned to its declaration
  { // level 2 scope
    asdf1(); // Yay I can use it!
  }
}
asdf0(); // Damn! I have access outside of the block scope due to hoisting! Should have used strict mode...

If we enclose the previous logic into a function however, we don't get hoisting to the global scope, only up to the function's scope.

function scopeBlocker() {
  var asdf0 = undefined;
  ...
  ...
}
asdf0(); // Exception! Wasn't hoisted all the way up to the global scope!

Now, to come to your example, because you were not using strict mode you fell into the trap of old block scoping. In your first attempt, the second declaration inside the first block scope was hoisted all the way up to its enclosing scope and replaced the first foo declaration. Now because hoisting happens only once, the following declarations were not hoisted thus not replacing anything. In the second case, adding the top level block scope forced the first foo declaration to be hoisted to the global enclosing scope and thus the second foo declaration was now not hoisted because the foo name had already been hoisted.

It is indeed a weird behavior, but I believe that's what's happening here.

PentaKon
  • 4,139
  • 5
  • 43
  • 80
  • ya thats what i not able to figure out. Why in the first case it logs 1 and second case 4. Becuase `foo()` logging 4 is defined first and should be captured in window and not the one logging 1. – Manish Jun 11 '17 at 17:31
  • @Manish have amended my answer – PentaKon Jun 11 '17 at 21:34
0

I think it depends on where you are running your code. Maybe this feature isn't supported just yet. Your code snippet logs 4, 1, 2, 2, 2 (Firefox browser).

Logically your code should log as you say.

Where exactly do you test the code?

Spitzbueb
  • 5,233
  • 1
  • 20
  • 38
  • I tested in Chrome and IE11. Issue is the last `console.log()` in both snippets that logs a different value then expected for the first snippet. But second snippet logs the correct expected values – Manish Jun 09 '17 at 07:00