ECMAScript 6 bounded block function Strange behavior
I've been reading new features in ECMA6. One of the new features is Limited Block Features . This means that we can have the same name for functions in different blocks. As in the code below block1 foo()
logs 1
, then similar to block2 foo()
logs 2 and block0 foo() or global scope
logs 4. And this is the expected result. But what I can't figure out is why the last console.log(foo())
log is 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 if I add the above code in another block, it works as expected. It confuses me a little. What is actually causing this behavior?
{//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
}
source to share
Completely modified answer for an in-depth explanation
First of all: the problem occurs due to the fact that you are not using the mode use strict
. What is the difference in mode strict
and non-strict
in this case? The difference is how the simplest viewport is handled.
In both modes, the parenthesis block creates a region. Declarations within this scope can be viewed within the scope itself and inside other private scopes.
{ // level 1 scope
function asdf() {alert("asdf");}
{ // level 2 scope
asdf(); // Yay I can use it!
}
}
However , in ES5 and ES6, a non strict
simple block area is not considered a true enclosing area. Declarations within a simple block scope are raised all the way to the first true enclosing scope they can find. In these cases, the true enclosing scope is either the scope or the global scope.
The above example in mode is non strict
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 add the previous logic in function
, we don't get hoisting into the global scope just up to the function 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 are not using strict mode
, you have fallen into the trap of an old survey. In your first attempt, the second ad within the first block's scope was raised all the way to its enclosing scope and replaced the first ad foo
. Now, since the lift only happens once, the following ads have not been raised, so they don't replace anything. In the second case, the addition of a top-level block scope caused the first ad to foo
be hoisted into the global enclosing scope, and therefore the second ad foo
was now not hoisted because the foo
name had already been hoisted.
This is really strange behavior, but I believe what is happening here.
source to share
You need to make sure that strict mode is enabled. Since it is enabled in modules by default, I guess your code example does not show this. The Kangax compatibility chart shows the corresponding 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
source to share