JavaScript: differences between using var, let and const.

A close look and explanation at how to properly use the new ES6 JavaScript variable declaration keywords.

JavaScript Logo
JavaScript Logo

Introduction

With the advent of ES6 (EcmaScript2015), among the many new features, there have been introduced two new keywords to declare variables: let and const. Let's now look at them one by one to discover their differences using practical examples.

emoji 👉 Var

Everyone is already used to the good old var keyword, but a refresh is always a good idea.

About the var keyword:

  • Its enclosing scope is the Function scope;
  • It can be re-declared;
  • It can be re-assigned;
  • It hoists;
  • It does not suffer from TDZ (See: [1]).
//Using `var`.
function test(){

	console.log(x); // x === undefined <- It hoists.
	
	for( var z = 0; z < 3; z++ ){
	
		setTimeout(function(){
			console.log(z)
		}, 500); // 0 -> 3 | 1 -> 3 | 2 -> 3, It needs a closure to capture the value!
	
	}
	
	console.log(y); // y === undefined <- It's enclosing scope is the first function scope.
	
	if(true){

		var y = 1;
	
	}
	
	var x = 3;
	var x = 4; // It can be re-declared.
	x = 5; // It can be re-assigned.
	
	console.log(y); // y === 1
	console.log(z); // z === 3
	console.log(x); // x === 5
	
}

emoji 👉 Let

Let is a one of the new keywords and it introduces some really interesting features and gotchas:

  • Its enclosing scope is the Block scope;
  • It can't be re-declared;
  • It can be re-assigned;
  • It hoists;
  • It suffer from TDZ (See: [1]).
//Using `let`.
function test(){
	
	console.log(x); // Error! <- It hoists, but it can't be accessed (TDZ [1]).
	
	for( let z = 0; z < 3; z++ ){
	
		setTimeout(function(){
			console.log(z)
		}, 500); // 0 -> 0 | 1 -> 1 | 2 -> 2, Yey! emoji 🎉 It captures the values without the need of an new closure!
	
	}
	
	console.log(y); // Error! <- It is outside its scope!
	
	if(true){

		let y = 1;
	
	}
	
	let x = 3;
	let x = 4; // Error! <- It can't be re-declared!
	x = 5; // It can be re-assigned.
	
	console.log(y); // Error! <- It is outside its scope!
	console.log(z); // Error! <- It is outside its scope!
	console.log(x); // x === 5
	
}

emoji 👉 Const

Const is the other new keyword, and it is really straightforward:

  • Its enclosing scope is the Block scope;
  • It can't be re-declared;
  • It can't be re-assigned (See: [2]);
  • It hoists;
  • It suffer from TDZ (See: [1]).
//Using `const`.
function test(){

	console.log(x); // Error! <- It hoists, but it can't be accessed (TDZ [1]).
	
	for( const z = 0; z < 3; z++ ){ // Error! <- Can't be re-assigned!
	
		setTimeout(function(){
			console.log(z)
		}, 500);
	
	}
	
	console.log(y); // Error! <- It is outside its scope!
	
	if(true){

		const y = 1;
	
	}
	
	const x = 3;
	const x = 4; // Error! <- It can't be re-declared!
	x = 5; // Error! <- It can't be re-assigned! [2]
	
	console.log(y); // Error! <- It is outside its scope!
	console.log(z); // Error! <- It is outside its scope!
	console.log(x); // x === 3
	
}

Summary table

FeatureVarLetConst
Enclosing scopeFunction scopeBlock scopeBlock scope
Re-declarationYesNoNo
Re-assignmentYesYesNo ([2])
HoistsYesYesYes
Temporal Death Zone [1]NoYesYes

emoji 📔 Footnotes

[1] The TDZ (Temporal Death Zone) has been introduced along with let and const. In fact, the var declaration does not suffer from this issue. In particular the TDZ is the time that spans between the context creation (the variable is created and hoisted) and the assignment instruction (the variable has actually given a value). During this time a variable created with those keywords is inaccessible and therefore will throw and error trying to read it.

[2] You can't reassign a constant, but you can change its values in case it is an Object or an Array (See code below).

//Re-assigning `const`.
function test(){

	const MY_CONST_VAL = 1;
	const MY_CONST_ARR = [ 1, 2, 3 ];
	const MY_CONST_OBJ = { a: 1, b: 2, c: 3, d: { a: 1, b: 2 } };
	
	MY_CONST_VAL = 2;			// emoji 💥 Error! <- Can't be re-assigned!
	MY_CONST_VAL = [2];			// emoji 💥 Error! <- Can't be re-assigned!
	MY_CONST_VAL = {b: 2};		// emoji 💥 Error! <- Can't be re-assigned!
	
	MY_CONST_ARR = [2];		// emoji 💥 Error! <- Can't be re-assigned!
	MY_CONST_ARR[0] = 0;		// emoji 🎉 Works!
	MY_CONST_ARR[4] = 4;		// emoji 🎉 Works!
	MY_CONST_ARR.push(5)		// emoji 🎉 Works!
	
	MY_CONST_OBJ = {b: 2};		// emoji 💥 Error! <- Can't be re-assigned!
	MY_CONST_OBJ.a = 0;		// emoji 🎉 Works!
	MY_CONST_OBJ.d = 4;		// emoji 🎉 Works!
	delete MY_CONST_OBJ.d		// emoji 🎉 Works!
	
}

emoji 📚 Further readings

List of all the new ES6 features

The hoisting process

For updates, insights or suggestions, feel free to post a comment below! emoji 🙂


Responses

Latest from Web Engineering see all

JavaScript: in depth practical explanation on closures. | cover picture
JavaScript: in depth practical explanation on closures.
Created on 21 July 2018.
A deep look at JavaScript closures with explanations and hands-on code examples.
JavaScript: how to find a key-value pair in a nested object. | cover picture
JavaScript: how to find a key-value pair in a nested object.
Created on 17 June 2018.
How to recursively traverse a JavaScript nested and unordered object to find if a value exists at a specific key.
Browser Rendering Queue in-depth. | cover picture
Browser Rendering Queue in-depth.
Created on 10 January 2018, updated on 17 June 2018.
An in-depth full explanation on how browsers manage to push pixels on the screen, and how to improve performance.
×