0

var x = true;

if (x == true) {
  console.log(typeof(x));
  let x = false;
};

console.log(x);

Sorry new to programming and getting this error: Error: x is not defined.

I am extremely confused.

j08691
  • 204,283
  • 31
  • 260
  • 272
Abdulrehman
  • 385
  • 1
  • 4
  • 6
  • https://stackoverflow.com/questions/762011/whats-the-difference-between-using-let-and-var-to-declare-a-variable-in-jav – epascarello May 10 '18 at 19:46
  • 1
    you have x defined outside the `if` statement.... the let might be changing its scope. If what you're trying is just assign a value just due `x = false` – David Espino May 10 '18 at 19:46
  • 1
    The error message for Firefox is a bit more usefull here `ReferenceError: can't access lexical declaration 'x' before initialization` – t.niese May 10 '18 at 19:50
  • Have a look at the *The Temporal Dead Zone* [TDZ](https://stackoverflow.com/a/39427692/8122487) – RaphaMex May 10 '18 at 19:58

3 Answers3

4

It doesn't look like it, but the two variables you declared are different. Your var declaration is one of them, and the let declaration is different. You've confused yourself by naming them with the same variable.

The let x exists strictly within the if block. This let x is the only x that can exist in that block now and you've tried to use that x before you declared it when you attempt a console.log.

You can get away with using variables before you declare them with var, but let and const won't let you.

Also, I highly recommend you don't mix var and let in your code. You have to really understand what's going on if you do.

var x = true; // first x variable

if (x == true){ // first x variable
  console.log(typeof(x)); // second x variable, used before declaration
  let x = false; // second x variable created in `if` block
};

console.log(x); // first x variable

edit: Your follow up question is still 2 different variables. The only thing you've done is coerced the code to make it look EVEN MORE like the same variable.

var x = true; // first x
if (x == true) { // first x
    let x = false; // second x
    console.log(x); // second x, false
};
console.log(x) // first x, true
Andrew
  • 7,201
  • 5
  • 25
  • 34
  • It might be a bit clearer if you actually name the variables differently in the code. E.g. `x1` and `x2`. It will become quite clear what is happening then. – VLAZ May 10 '18 at 19:51
  • @vlaz I agree 100%, but I don't think that's the origin of confusion. I'm pretty sure OP thinks that the `x` is the same variable in every line of sample code provided – Andrew May 10 '18 at 19:53
  • Indeed, I agree. I just think that if you post an example with the variables named differently it's a bit easier to grasp at a glance. – VLAZ May 10 '18 at 19:55
  • @Andrew I guess so, so I am confusing myself, I started with a global variable, and then went into the if block and tried changing the value on a local scope. I guess the mistake I was making was I was treating it as a single variable, when in fact there are two variables. I guess it is safe to conclude you cannot change the scope of a variable once its created? Once again everyone, Thanks! – Abdulrehman May 10 '18 at 19:57
  • @Abdulrehman Yes this is correct. But as you can see, scope can be overwritten when you switch around declarations using the same variable name. This is why a lot of linters don't let you use "shadow variables". Shadow variables are variables that you declare with the same name in a function. Also, don't mix `let` and `var` in your code. Use one or the other or you're going to confuse the hell out of yourself unless you know exactly what you're doing – Andrew May 10 '18 at 20:01
  • @Andrew Sorry, here I'll bug just a bit more, when you said: "But as you can see, scope can be overwritten when you switch around declarations using the same variable name. " Isn't this not true, because when you switch the declaration statement here: var x = true; if (x == true) { let x = false; console.log(x); // false }; You are still creating a new variable and then accessing, not changing the scope of the original. – Abdulrehman May 10 '18 at 20:06
  • @Abdulrehman Updated – Andrew May 10 '18 at 20:10
1

This one's a little tricky to explain, but I'll try;

var x = true;

if (x == true) {
  console.log(typeof(x)); // <-- Error: x is not defined.
  let x = false;
};

console.log(x); // true

The last line is actually fine because you defined x up top.

What's happening inside the if statement is that JavaScript looks ahead and sees that you've declared x (with let or const) somewhere in that scope, but it hasn't yet reached that line.

So merely having a let or const somewhere further down in the same scope is causing that variable to not be defined.

If you move it up a line, everything is fine:

var x = true;

if (x == true) {
  let x = false;
  console.log(x); // false
};

console.log(x); // true

And if you don't redeclare x, it's also fine:

var x = true;

if (x == true) {
  let y = false; // different variable name (won't "shadow" the `x` from above)
  console.log(x,y); // true false
};

console.log(x); // true

var behaves differently:

if (true) {
  console.log(y); // undefined
  var y = false; // `var` is less strict. The declaration of `y` is hoisted to the top of the scope, but it's not assigned until this line
  console.log(y); // false
};
mpen
  • 272,448
  • 266
  • 850
  • 1,236
-1

The part of explanation that everyone is missing is that declarations are hoisted, which means that the declaration is brought to the beginning.

This is especially true with var. For let things are a little bit different and the term hoisting is not the most accurate. But it is useful to understand what is going on.

When executed, your code will look something like this (not exactly but gives the idea)

var x = true;

if (x == true) {
  let x // hoisted at top of scope, for let scope is this if block
  console.log(typeof(x));
  x = false;
};

console.log(x);

Now everything should make sense: typeof(x) refers to the x declared with let but not yet initialized.

To be clearer this is the reason why the x inside of typeof refers to the x declared in the row below and not the one at the beginning: because the declaration of the existence of x inside that block is brough to the beginning of such block

Francesco
  • 4,052
  • 2
  • 21
  • 29
  • 2
    This is not true. `let` is not hoisted. That's why the error is thrown – Andrew May 10 '18 at 19:58
  • `let` declaration is hoisted, its initialization is not, that is why the error is thrown: https://stackoverflow.com/questions/31219420/are-variables-declared-with-let-or-const-not-hoisted-in-es6 – Francesco May 10 '18 at 20:00
  • @Francesco No. It's hoisted with `var` but not `let` -- otherwise it would have printed "undefined". Try **running** your code, it gives a different result than OP's. – mpen May 10 '18 at 20:02
  • I'm aware of this (I promise), but in layman's terms, saying "hoisting doesn't exist in `let` is the easiest way to convey the concept because there is no delineation between hoisting and initialization when using `var`. Initialization vs hoisting is a textbook explanation and for the sake of a newcomer, saying "hoisting doesn't occur" is the best way to convey the concept. @mpen Francesco is technically correct. – Andrew May 10 '18 at 20:04
  • Sorry, I should be more clear... it is "hoisted" in the sense that merely having ` let` further down will cause an error. So we actually having three things going on, but I'm not certain of the correct terminology. There's "declaration" (x is a thing that exists, always hoisted), "initialization" (set x to undefined, hoisted with var but not let/const), and "assignment" (set x to false, never hoisted) – mpen May 10 '18 at 20:11
  • I agree on the fact that hoisting is probably not the most accurate term, and the executed code is _something like_ what I wrote. But I think that the confusion in the OP is due to the lack of knowledge of hoisting concept which applies partially in this case – Francesco May 10 '18 at 20:15
  • Thanks for the suggestions, I tried to make the answer more precise and understandable – Francesco May 10 '18 at 20:21