0

Please note this is not duplicate of existing var vs let scope. I'm aware of the scope of var and let declarations and differences.

But below scenario I could not justify with the understanding I had with let and var difference.

In the below code, function foo accepts argument by name 'x' which has implicit let scope - as I cannot redeclare same variable name using let inside that function (uncommenting last line in function foo will throw JS error)

"use strict";

function foo(x) {
    console.log('Inside function x:', x);
    var x = 30; // 'x' redeclared overwriting argument/parameter 'x'
    console.log('Redeclared x:', x);
    // let x = 400; // uncommenting this line throws error even if you remove 'var x = 30;'
}

foo(100);
// global
let y = 100;
console.log('y:', y);
// var y = 300;

Executing the above code with two lines commented out works perfectly, and you can see the output as:

Inside function x: 100      index.js:4 
Redeclared x: 30            index.js:6
y: 100                      index.js:13

Uncommenting last line // var y = 300; will throw error.

Question is: Why redeclaring 'x' using 'var' inside function foo works but throws error when 'y' is redeclared in the global scope using 'var'

Chandre Gowda
  • 870
  • 1
  • 7
  • 24
  • Possible duplicate of [What's the difference between using "let" and "var"?](https://stackoverflow.com/questions/762011/whats-the-difference-between-using-let-and-var) – Code Maniac May 31 '19 at 10:41
  • @code maniac, I'm not asking the basic difference between 'let' and 'var', the behaviour of them redeclaring within function and outside function is confusing, inside function it is allowing to redeclare an argument using var, but outside function is it throwing error. Please explain or point me to the document or sample where it is clear to understand – Chandre Gowda May 31 '19 at 10:45
  • simple thumb rule is in a scope if you have defined any variable with `let` you can not redefine it, don't get confused with function argument they behave as var defined with var as far as i know – Code Maniac May 31 '19 at 11:07
  • @CodeManiac, it is not behaving as `var` in the function argument, it is behaving as `let`, this can be tested by commenting out `var x = 30;` and uncommenting `let x = 400;` inside the function foo, it throws error as you cannot redeclare variables which is already declared using `let` – Chandre Gowda May 31 '19 at 11:21
  • Possible duplicate of [Cannot use let to define a variable with the same name of function's parameter](https://stackoverflow.com/questions/52380673/cannot-use-let-to-define-a-variable-with-the-same-name-of-functions-parameter) – adiga May 31 '19 at 11:28
  • @adiga, though the link points to similar question (thanks for pointing), it does not answer clearly why it behaves differently inside a function and outside. If it was ignoring `var` key inside function - why it is not ignoring outside function. Or, if it is allowed to declare variables using `var` when it is already available as parameter (lexical scope `let`), then why does it throw error in global scope. – Chandre Gowda May 31 '19 at 11:51
  • It doesn't behave differently inside and outside the function. In both places, `let` will throw an error if an identifier with the same name already exists. – adiga May 31 '19 at 11:56

1 Answers1

1

The var declaration syntax is original to the language, and has fairly permissive rules. The let and const declarations are newer and more strict. You cannot re-declare variables with let or const no matter how they were declared in the first place. And if a variable is declared with let or const, a subsequent var declaration is also an error.

Declarations via let and const will not allow references to the variables before the declaration; that's why you get the error mentioned in your first example. In other words,

console.log(x);
let x = 0;

is an error because x is referenced before the declaration.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • 'let' does not hoist the variables, but in the function it is already implicitly declared as 'let x' in the parameter and expectation is that it should have thrown error when there is a redeclaration of x inside function, but it is working fine without error inside function, but throws error outside function. Wanted to know how is this behaviour is different within function and global – Chandre Gowda May 31 '19 at 10:42
  • @ChandreGowda I really don't understand what you mean. `let` *does* hoist the declaration to the enclosing block, but references before the declaration are errors. – Pointy May 31 '19 at 10:45
  • I was saying 'variable hoisting' happens only for variables declared using 'var' and hence you will be able to access/print it before declaration, in your sample you have declared x using 'let' and as 'let' variables is not hoisted, it will not have reference to access and hence throw error. – Chandre Gowda May 31 '19 at 10:49
  • @ChandreGowda it is not true that hoisting only happens with `var`. – Pointy May 31 '19 at 10:50
  • among 'var', 'let' and 'const' declaration - hoisting happens only for 'var', please try it out in the console. You can see error like 'Cannot access 'z' before initialization' for variables declared with let or const and try to console.log before it is declared. – Chandre Gowda May 31 '19 at 10:53
  • 1
    Variable declarations with `let` and `const` are hoisted to the enclosing block, but references before the declaration are not allowed. That is clear if you try to re-declare such variables. – Pointy May 31 '19 at 10:58
  • `{ console.log('y: ', y); const y = 200; }` this does not work, 'y' is not accessible, which clearly shows hoisting does not happen within block for `const` and `let` – Chandre Gowda May 31 '19 at 11:15
  • @ChandreGowda It **does** happen; that's why it's an error. You should read the language spec, particularly the part about the "temporal dead zone". – Pointy May 31 '19 at 11:22
  • @ChandreGowda [Are variables declared with let or const not hoisted in ES6?](https://stackoverflow.com/questions/31219420) – adiga May 31 '19 at 11:23