45
console.log(a) //output:ƒ a(){}
var a = 1;
function a(){};
var a = 10;
console.log(a) //output:10

====================

var a = 1;
if(true){
  function a(){};
  var a = 10;
}
console.log(a) // this code throws Uncaught SyntaxError: Identifier 'a' has already been declared

both above code snippets are same except the if block.why does the latter throws error when its permissible in javascript to delcare same variable twice in the same scope with var as below

 function a(){};
 var a = 10; //no error

Also for a slightly different scenario after removing var from `var a = 10 in the above code ,then it works fine but output is surprising

 var a = 1;
 if(true) {
   function a(){};
   a = 10;
 }
 console.log(a) //output:ƒ a(){}

I am surprised to see this output as I am expecting 10 ..because two variables declared inside the if block refer to the same variable declared above as javascript var doesnt respect block scope but functional scope...so why not the output for above should be 10? where as the below code outputs 10 as i expected when replaced the function definition with function expression.

  var a = 1;
  if(true) {
    var a = function(){ console.log() }
    a = 10;
  }
  console.log(a) //output:10
Alexander Chernin
  • 446
  • 2
  • 8
  • 17
venkata
  • 560
  • 1
  • 5
  • 14
  • 1
    Well, function declarations inside `if` blocks were invalid in ES5, so using them will have [ES6 semantics](https://stackoverflow.com/a/31461615/1048572) with stricter rules about duplicate declarations. – Bergi Apr 11 '18 at 12:34
  • Related, if not duplicate: [Why does redeclaring a function identifier within a try block throw a SyntaxError?](https://stackoverflow.com/q/44897189/1048572) – Bergi Apr 11 '18 at 12:43

3 Answers3

33

This is surprising as javascript var doesn't respect block scope but functional scope...

Sure, but you didn't use var for the declaration of a in the block scope. You used a function declaration, which does respect block scopes (otherwise it would be completely invalid code, as in ES5 strict mode).

It's permissible in javascript to declare same variable twice in the same scope with var as below

Same applies here. The function declaration in the block uses ES6 declaration semantics (like let or const), which does not allow redeclarations.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
12

Case 1

console.log(a) //output:ƒ a(){}
var a = 1;
function a(){};
var a = 10;
console.log(a) //output:10

Will be rendered as

var a;
a = function(){}; // now a holds the value as a function
console.log(a); // output : f a(){}
a = 1; // a is a var that holds value 1
a = 10; // a is a var that holds value 10
console.log(a); // output : 10

Case 2

var a = 1;
if(true){
   function a(){};
   var a = 10;
}
console.log(a)

Will be rendered as

var a;
a = 1;
if(true) {
    a = function() {};
    let a; // The function declaration in the block uses ES6 declaration semantics (like let or const), which does not allow re-declarations.
    var a; // throws Uncaught SyntaxError: Identifier 'a' has already been declared
    a = 10;
}
console.log(a);

Case 3

var a = 1;
if(true){
    function a(){};
    a = 10;
 }
console.log(a)

Will be rendered as

var a;
a = 1;
if(true) {
    a = function() {};
    let a;
    a = 10;
}
console.log(a); // output : f a(){}

Case 4

var a = 1;
if(true){
    var a= function(){console.log()}
    a = 10;
}
console.log(a)

Will be rendered as

var a;
a = 1;
if(true) {
    a = function(){console.log()}
    a = 10;
}
console.log(a) // output:10

Case 5

var a = 1;
if(true){
    function a(){};
    a = 10;
    console.log(a) 
}
console.log(a) 

Will be rendered as

var a;
a = 1;
if(true){
    a = function() {};
    let a;
    a = 10;
    console.log(a); // output:10
}
console.log(a); // output : f a(){}
Nikhil Aggarwal
  • 28,197
  • 4
  • 43
  • 59
  • More like `let a` in case 3. – Bergi Apr 11 '18 at 12:44
  • 1
    @Bergi - Thank you. I have taken reference from your answer while posting my understanding. Hope it is okay with you. If not, I will be happy to remove my answser. – Nikhil Aggarwal Apr 12 '18 at 05:23
  • @PrashantTapase - Thank you. I have updated my answer for better understanding and you can now see the behavior with the false statements as well. – Nikhil Aggarwal Apr 12 '18 at 06:06
  • 1
    @nikhilagw It's fine :-) – Bergi Apr 12 '18 at 08:18
  • @nikhilagw great..its very clear and Thanks for the answer with detailed scenarios ...could you also please mention how this piece of code is rendered ...if(true) { function b(){} } ? Thanks in advance – venkata Apr 12 '18 at 09:19
  • I am asking this because when i execute this code in chrome console ..console.log(b); //output:undefined if(true) { function b(){} } console.log(b); //output:ƒ b(){} so I am doubtful if function declarations are treated like let/const as b is available outside the block as well – venkata Apr 12 '18 at 09:27
  • @venkata - In your case it will be like this. `var b; console.log(b); if(true) { b = function(){}} console.log(b);` Because of this the first console gives undefined and the second gives the function. The declarations are hoisted to parent function scope, however, the value is set in the scope only. Any function definition in the block, basically ends up blocking the variable name in that block just like let and const do, however, the association of the function is always with the parent function scope. Hope it clarifies. – Nikhil Aggarwal Apr 12 '18 at 09:53
  • 1
    Its very clear now.Thanks a lot nikhilagw.Thanks @Bergi – venkata Apr 12 '18 at 10:46
  • @venkata, nikhilagw: the [details of sloppy mode block scope function declarations in browsers](https://stackoverflow.com/a/31461615/1048572) are just weird. You should use strict mode and ignore what you just saw :-) – Bergi Apr 12 '18 at 11:05
  • @Bergi - Agreed. Makes sense. :) – Nikhil Aggarwal Apr 12 '18 at 12:14
5

The simple solution to this is to use IIFE

(function() {
var sahil = {
  checkThis: function() {
    console.log(this);

    function checkOther() {
      console.log(this);
    }
    checkOther(); // checkThis() function called in "global context", will
                  // return "this" as "window"
  }
};
var moo = sahil.checkThis;
moo(); // moo() function called in "global context", will return "this" as "window" })();
Nick
  • 138,499
  • 22
  • 57
  • 95
Mohd Sahil
  • 385
  • 3
  • 3