Andrew Hill

@andrewjrhill

Getting to Grips with ES6: Variables

Part I: Understanding var, let, const and scope

Click here to view Part II: Understanding fat arrow functions

Preface

This article is designed as a “get started” read for beginner to intermediate developers who wish to gain a better understanding of ES6 variables.

As part of my “Getting to Grips with ES6” series, I aim to create a reference containing straight forward explanations ordered in short sections to help us understand and apply these concepts directly to our current development processes.

Written by Andrew Hill
You can find me on LinkedIn, Twitter, Instagram, and GitHub.

The ‘var’ Statement

While the var statement is not new to ES6, I strongly recommend reading through this refresher to help us better understand the concepts that the const and let statements introduce.

At its core, the var statement allows us to define, update and redefine variables. Variables declared with var are considered function scoped.

Define and Update

We can define and update variables declared with var without any errors.

var test = 100;
console.log(test);
// Output: 100
test = 200;
console.log(test);
// Output: 200

Redefine (Allowed)

We can also define an entirely new variable with the same name to override our previous variable with no errors.

var test = 100;
console.log(test);
// Output: 100
var test = 200;
console.log(test);
// Output: 200

Scope

Variables defined using var are scoped as follows:

  • Function scoped if they are defined within a function making them accessible only inside the function in which they are defined.
  • Globally scoped if they are not defined within a function making them accessible everywhere.

Consider the following example:

function scopeTest() {
var test = 100;
console.log(test);
}
scopeTest();
// Output: 100

The test variable is considered a local variable, function scoped to the scopeTest() function. We are able to access the value stored in the variable test as both the variable declaration and the console.log() exist within the same scopeTest() function.

Using a modified version of this example allows us to illustrate how this variable is not accessible outside of the function it is scoped to:

function scopeTest() {
var test = 100;
}
scopeTest();
console.log(test);
// Output: [Error] 'test' is not defined.

We are also allowed to update a variable at a higher scope to gain access to the value outside of the function. For reasons beyond the scope of this article, this is often considered bad practice and should be avoided unless absolutely necessary.

var test;
function scopeTest() {
test = 100;
}
scopeTest();
console.log(test);
// Output: 100

The “bad” side to var

So why bring in the new const and let statements in ES6? Because any variable defined outside of a function using var is globally scoped.

Global scoping is bad as it introduces:

  • Naming collisions - when you or or team cannot keep track of variable names, variables unintentionally get redefined and thereby overwritten.
  • Weakened Security - Especially on the web, every user inherently has access to the global scope. Access to this allows users to see and modify your variables.
  • Poor performance - Although arguably negligible, the JavaScript engine will look for a variable at a higher scope if it cannot find it where it is being currently being called. If your variables are all globally scoped, the engine will consistently need to make its way to the global window object in order to find the value it is looking for.

The ‘let’ Statement

The let statement allows us to define and update variables much in the same way the var statement does, but it does not allow us to redefine variables that are already defined in the same scope. Variables defined with let are also considered block scoped.

Define and Update

We can define and update variables declared with let without any errors.

let test = 100;
console.log(test);
// Output: 100
test = 200;
console.log(test);
// Output: 200

Redefine (not allowed)

Attempting to define a new variable with the same name as our previously defined let variable is prohibited and results in errors.

let test = 100;
console.log(test);
// Output: 100
let test = 200;
// Output: [Error] Identifier 'test' has already been declared.
console.log(test);
// Output: 100

The ‘const’ Statement

The const statement allows us to define variables only. Unlike let which was made to be updated, const prohibits updating variables entirely. Furthermore, like let, const prohibits the redefining of variables that have already been defined in the same scope. Variables defined with const are considered block scoped.

Define (allowed) and Update (not allowed)

We can define const variables but are prohibited from updating the values assigned to the variable. Attempting to update a const variable will result in errors.

const test = 100;
console.log(test);
// Output: 100
test = 200;
// Output: [Error] Assignment to constant variable.
console.log(test);
// Output: 100

Redefine (not allowed)

Attempting to define a new variable with the same name as our previously defined const variable is prohibited and results in errors.

const test = 100;
console.log(test);
// Output: 100
const test = 200;
// Output: [Error] Identifier 'test' has already been declared.
console.log(test);
// Output: 100

Mutability

There is a growing misconception that const is entirely immutable meaning you can never update an objects properties once it is assigned to a const variable. When it comes to objects and const, you cannot redefine the const variable, but you are allowed to update the properties of the object.

In the below example, we attempt to assign a new object to the test variable. As previously covered, this is not allowed and we encounter errors.

const test = {
milliseconds: 1862,
createdBy: 'Andrew Hill',
}
test = {
milliseconds: 1862,
createdBy: 'Jane Doe',
}
// Output: [Error] Assignment to constant variable.
console.log(test);
// Output: Object { milliseconds: 1862, createdBy: 'Andrew Hill' }

However, in this example we are able to update the properties of the test variable without any problems.

const test = {
milliseconds: 1862,
createdBy: 'Andrew Hill',
}
test.createdBy = 'Jane Doe';
console.log(test);
// Output: Object { milliseconds: 1862, createdBy: 'Jane Doe' }

If you are looking for “truly” immutable objects, take a look at the Object.freeze() method which prevents new properties from being added, existing properties from being removed, and prevents existing properties, their enumerability, configurability, and writability, from being changed.

Scope: const and let

Variables defined using const and let are considered block scoped, meaning they are available to you inside the code block in which they are defined. A JavaScript code block is anything that falls within curly braces {}. Objects and functions both contain blocks and it is perfectly valid to have a block exist on its own.

Consider the following example. Note that we are using let in the below example, but the same rules apply to the way const is scoped:

function scopeTest() {
let test = 100;
console.log(test);
}
scopeTest();
// Output: 100

In this example, the test variable is considered a local variable, scoped to the block created by the scopeTest() function. We are able to access the value stored in the variable test as both the variable declaration and the console.log() exist within the same scopeTest() function block.

Using a modified version of this example allows us to illustrate how this variable is not accessible outside of the function block it is scoped to.

function scopeTest() {
let test = 100;
}
scopeTest();
console.log(test);
// Output: [Error] test is not defined.

Multiple variables can be defined with the same name using const and let so long as they do not fall within the same block. Doing this will result in two entirely different variables existing with the same name at different scopes.

let test = 100;
console.log(test);
// Output: 100
function scopeTest() {
let test = 200;
console.log(test);
// Output: 200
}
console.log(test);
// Output: 100

Final Thoughts

So what should you use when? There are some differing opinions on this matter which you can dig into with a few quick Google searches. Ultimately, I choose to use them in the way Mathias Bynens describes in his article:

  • Use const for all variable declarations by default.
  • Use let only if rebinding is needed.
  • var should not be used in ES6

Thanks for reading. If you liked this article please consider supporting future installations of this series by recommending it. 👏

Click here to view Part II: Understanding fat arrow functions

More by Andrew Hill

Topics of interest

More Related Stories