A Gentle Introduction to ES6
You must understand var to grasp the benefits of let / const. Let’s rewind.
Review: Variable Declarations
It’s important to intentionally declare your variables within a specific scope, using var, to keep your code clear and maintainable.
var x = "outside";function foo() {var x = "inside";console.log(x);}
foo(); // insideconsole.log(x); // outside
Above code properly declares x both outside and inside the function using var. What happens without var in foo?
var x = "outside";function foo() {x = "inside";console.log(x);}
foo(); // insideconsole.log(x); // inside
Uh oh! x outside the function was overwritten by x inside the function because we didn’t specify that x was to be scoped only to foo!
Hoisting Best Practices
Declare variables using var at the top of the current scope.
OK:
console.log('sup')var i = 0;
Better:
var i = 0;console.log('sup')
Review: Hoisting
Variables declared using var are always hoisted to the top of their scope.
console.log(j); // ReferenceError: j is not defined
console.log(i); // undefinedvar i = 0;
The variable j was never declared, so we get an error saying “I’ve never heard of j!”.
i was declared before being logged due to hoisting. Here is how the interpreter executed it:
var i;console.log(i);i = 0;
The interpreter moved (e.g. “hoisted”) the variable declaration to the top of the scope.
However, the variable was not assigned to 0 yet. undefined says “I know i exists, but I don’t know what value i points to because you didn’t assign it to anything”.
Supplement1: What if _var_ refers to a function?Supplement2: Hoisting doesn’t physically “move” your code — MDN
Function Scope
var's are function-scoped: scope is limited to the function it was defined in.
function foo() {var i = 0;}
console.log(i); // ReferenceError: i is not defined
i only exists within foo so we get an error: “I’ve never heard of i!”.
Block Scope
var’s are not block-scoped: scope is not limited to the block it was defined in.
var i = 0if (true) {var i = 1;}
console.log(i); // 1
i was still in the “global scope” within the if block. i's value was overwritten, which may have not been the intention.
let
let variables are block-scoped! Specific scope = less mistakes.
let i = 0;if (true) {let i = 1;}
console.log(i); // 0
Even though i was assigned to 1 in the if block, that assignment was local to the block and therefore our “global” i was still 0. The if block’s scope was separate from the global scope.
const == Constant
const restricts over-writing variables.
const i = 0;i = 1; // TypeError: Assignment to constant variable.
const doesn’t even let you declare a variable without assigning its (constant) value!
const i; // SyntaxError: Missing initializer in const declaration
const, like let, is block scoped.
if (true) {const i = 0;}
console.log(i); // ReferenceError: i is not defined
const does allow variable mutation (only objects/arrays are mutable in JS).
Array Mutation:
const a = [1];const b = a;console.log(a === b); // trueb.push(2);console.log(a === b); // trueconsole.log(a); /// [ 1, 2 ]
Object Mutation:
const obj = {};obj.i = 1;console.log(obj); // { i: 1 }
let/const Hoisting
let and const declarations are not hoisted!
EDIT: Technically they are hoisted, but they are not initialized to anything (
varis initialized toundefined).
console.log(a); // undefinedvar a = 2;
console.log(b); // Uncaught ReferenceError: b is not defined
console.log(c); // Uncaught ReferenceError: c is not definedlet c = 2;
This protects against variable declarations placed after references to variables.
isEqualTo5 has a bug — it always returns true.
function isEqualTo5(n) {return !(n - five);var five = 5;}console.log(isEqualTo5(4)); // true
The issue is that five isn’t assigned till after it’s referenced. It was declared so when it is referenced in the return statement, its value is undefined.
!(4 - undefined) === !(NaN) === true
This bug may be hard to catch. let/const to the rescue!
function isEqualTo5(n) {return !(n - five);const five = 5;}console.log(isEqualTo5(4)); // ReferenceError: five is not defined
const doesn’t hoist the declaration → error: reference before declaration→ prevents bug.
Specific scope = less errors + improved readability/maintainability.
