Jballin

@PrintSupWorld

var, let, or const?

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(); // inside
console.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(); // inside
console.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); // undefined
var 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 = 0
if (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); // true
b.push(2);
console.log(a === b); // true
console.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 (var is initialized to undefined).
console.log(a); // undefined
var a = 2;
console.log(b); // Uncaught ReferenceError: b is not defined
console.log(c); // Uncaught ReferenceError: c is not defined
let 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.

TLDR: var →🗑, const where you can 🔑, otherwise let's cool too! 🕶

More by Jballin

Topics of interest

More Related Stories