: Understanding the difference between lexical and block scoping The Goal : These are subtle differences that newer devs (like myself) may not know or be aware of since we've never really used the keyword Motivation var Let's start with an example using the below code sample. { ( ) { x = processVar(x) } { x = ; processVar(x) } } { .log(param); } logTenOrFifteen(); ( ) function logTenOrFifteen if true let 10 else let 15 ( ) function processVar param console Like every good developer, your instinct cries out to remove the duplication. We should only call once. While having it twice might not be the end of the world, imagine there are more functions that need to be called while passing in . You'd end up with a lot of duplication. Not very of you. processVar x DRY Being a clever engineer, you refactor the above block to { x; ( ) { x = ; } { x = ; } processVar(x) } { .log(param); } logTenOrFifteen(); ( ) function logTenOrFifteen let if true 10 else 15 ( ) function processVar param console With this clean implementation you give yourself a pat on the back and a 🌟. Now there's only one call to Hooray! Good software engineering wins the day! processVar Except, you might have a little voice in the back of your head nagging you about the variable declaration being separate from its assignment. This can especially be a problem if the is several lines above the statement. x let x; if/else This is where the difference between block scoping and lexical scoping matters. Since we're using , is block scoped. We can't achieve the following two goals when using block scoped variables. let x A single call to processVar Keeping the variable assignment only in the and blocks if else For #2 we'd ideally like to move the that appears outside of the into it. Doing so however would require calling in both parts of the conditional violating #1. let x; if/else processVar Block scoped variables only exist through the use of or when variables are declared. let const Quite the conundrum we have... A (bad) Solution If block scoping is the problem, how would lexical scoping help? Lexical scoping (using the keyword) would let us achieve both #1 and #2. var Here's how { ( ) { x = } { x = ; } processVar(x) } { .log(param); } logTenOrFifteen(); ( ) function logTenOrFifteen if true var 10 else 15 ( ) function processVar param console Now we have the variable initialization limited to just the block AND a single call to . How exciting! We got the best of both worlds! if/else processVar This solution works due to how javascript processes the function and how it hoists, initializes, and allows for the redefining of variables that are lexically scoped. When we use the example above, the declaration of is hoisted to the top of the function and since its lexically scoped, is also assigned a default value of . The remaining assignment to or is left in the block. The example above gets transformed by the javascript compiler to x undefined 10 15 if/else { x; ( ) { x = } { x = ; } processVar(x) } { .log(param); } logTenOrFifteen(); ( ) function logTenOrFifteen var if true 10 else 15 ( ) function processVar param console This is the same as our initial refactor where we moved the outside of the block and then assign values to the variable inside them. The only difference is that to achieve this effect we had to use (lexical scoping) instead of (block scoping). Javascript just gives some nice sugar for lexically scoped variables. let if/else var let Odd Behaviors This is something you should do. For one, the end result is the same. Except now since we used , the variable is accessible outside of the block (which makes sense, variables declared with are lexically scoped). This can lead to many issues that required the introduction of block scoped variables. The same difference between lexically and block scoped variables cause the following odd behaviors. NOT var var { x = ; ( ) { x = ; .log(x); } .log(x); } { x = ; ( ) { x = ; .log(x); } .log(x); } ( ) function foo let 10 if true let 12 console // outputs 12; console // outputs 10 ( ) function foo var 10 if true var 12 console // outputs 12 console // outputs 12 The first function acts as expected. We wouldn't expect the second console log to print 12 since we'd expect that the value for of 12 should only apply in the if statement and not outside. If it were simply a reassignment ( ) then we'd expect the output of both logs to be 12. foo x x = 12 The second function however prints 12 twice even though the variable is redeclared. This is due to the fact that in this case, the variable is lexically scoped since the variable was created with . foo var Lastly given this example { x = ; x = ; } { x = ; x = ; } ( ) function foo let 5 let 10 ( ) function foo var 5 var 10 the first function will throw an error that x cannot be redeclared. In contrast, the second function works. This is another key difference between lexical and block scoped variables. Lexical variables can be redeclared whereas block scoped variables cannot. To fix the first function, you'd have to remove the in the second assignment. I imagine javascript compiles the second foo function to something like foo let { x; x = ; x = ; } ( ) function foo var 5 10 since its lexically scoped. Key Takeaways Block scoped variables cannot be redeclared to a new value in the same scope as one another Lexically scoped variables can be redeclared to a new value in the same scope as one another Both lexically and block scoped variables are hoisted, Lexically scoped variables, when hoisted, are given a default value of making them accessible before they're defined. Ex: undefiend console.log(foo); var foo = 'hello'; // This would log undefined Block scoped variables, when accessed before assignment, throw a . Ex: ReferenceError console.log(foo); let foo = 12; // This would throw an error This was quite a deep dive but I hope it helped. I recommend reading for more insight into some of these differences. Will Vincent's article