How to CORRECTLY use let, var, const, and ‘use strict’ by Understanding Scopes

Robert Long
10 min readJan 21, 2020

Introduction

Defining variables is a critical part of writing any application. Variables can be thought of as wires or channels through which data can flow. In this sense, understanding how to define variables can dramatically affect the performance of your application. JavaScript is unique because it has a lot of exceptions when it comes to variables.

Scopes

In order to get a clearer understanding, we have to introduce scopes (a.k.a. blocks). Scopes can be understood as folders (in your computer file system). Using the folder analogy, imagine that you create a folder called scope1 and begin to put files v1, v2, v3, v4 into it. A directory to this folder would look as such…

● scope1/v1

● scope1/v2

● scope1/v3

● scope1/v4

Let’s transform this a little: let’s replace the forward slash (/) with a dot (.). Then, let’s declare scope1 (analogous to a folder). Now we have the following…

● let scope1 = {};

● scope1.v1

● scope1.v2

● scope1.v3

● scope1.v4

Does this look familiar? It should, this is JavaScript. There is a direct mapping between our folder structure and JavaScript’s syntax for accessing variables through an object (or in this case a scope). You can go further by introducing a nested scope within scope1. This is analogous to a subfolder.

● scope1.nested1 = {}

In doing so, we can have hierarchies of scopes which we will talk about later in greater depth.

So why is this important? Scopes are used everywhere in JavaScript. For instance, the body of a function is a scope. IIFE (immediately invokable function expressions) and classes (in ES6) exclusively utilize scopes. Loops and if statements also use scopes. Pretty much everything within curly braces {} are encapsulated in a scope. An object itself in JavaScript is composed of a scope. Scopes are important because they can organize variables allowing them to be more predictable both for you and the interpreter.

Var

But let’s take a step back; because this is actually not exactly how var works. Unfortunately, var the keyword that JavaScript initially used to declare variables is wrought with pitfalls and treachery for those unaware.

Pitfalls: default hoisting for loops

Take, for instance, this snippet which includes the for loop logic:

  1. for(var i=0; i<10; ++i) {
  2. console.log(i);
  3. }

This looks simple enough. It prints

0

1

2

3

4

5

6

7

8

9

Okay, but add let’s add another console.log(i) at line 4. Like so…

  1. for(var i=0; i<10; ++i) {
  2. console.log(i);
  3. }
  4. console.log(i);

What do you think will happen? Let’s stop and think about this for a second. The declaration of i should live within the scope of the for loop, so printing it should give us an undefined result.

0

1

2

3

4

5

6

7

8

9

9

As you can see from the results, our variable i lives outside of the scope of the for loop. This is not the expected outcome of most c-based programming languages. The reason why this is happening is because when we use var in the for loop, javascript is actually running the code more like so…

  1. var i=0;
  2. while(i<10) {
  3. console.log(i);
  4. i++
  5. }
  6. console.log(i);

Yes, you saw this correctly! This is actually how javascript will read the for loop when you use var. This is also called hoisting because it is taking the variable i outside the scope of the for loop.

Pitfalls: default hoisting for undeclared variables

Another implication of this hoisting behavior is that you may see variables being used before they are declared. Take, for instance, the following code:

  1. function isEven(n) {
  2. if (n % 2 == 0) {
  3. var msg = ‘Your input is even.’;
  4. } else {
  5. var msg = ‘Your input is odd.’;
  6. }
  7. return msg;
  8. }

Notie here that var msg is declared twice, once inside the scope of the if clause and again inside the else clause. However, notice that at line 9, it is being returned as if it were declared. This should be incorrect since we did not declare msg outside the scope of the if or else clause. However, because of the defualt behavior of hoisting, JavaScript will pull msg to the top of our isEven function, vary similar to what we saw earlier in the for loop. The following is actually more similar to what JavaScript is actually doing.

  1. function isEven(n) {
  2. var msg = ‘’;
  3. if (n % 2 == 0) {
  4. msg = ‘Your input is even.’;
  5. } else {
  6. msg = ‘Your input is odd.’;
  7. }
  8. return msg;
  9. }

What’s more is, you can technically even start using a variable without even declaring it with var in this manner.

  1. p = ‘I am setting a value to p which was not formally declared by var’;
  2. console.log(p); // this works just fine
  3. var p = ‘Now we’re declaring it.’

Again, because JavaScript is hoisting the values up to the top automatically, it allows for these strange behaviors. This closer to what JavaScript is actually doing behind the scenes

  1. var p = ‘’;
  2. p = ‘I setting a value to p which was not formally declared by var’;
  3. console.log(p); // this works just fine
  4. p = ‘Now we’re declaring it.’

To avoid this strange behavior, we will make use of the ‘use strict’ at the top of our code document. This was introduced in ECMA5 for the var keyword.

So why is hoisting used?

Normally, hoisting is used to optimize code when you only need to do a calculation once before entering the loop. For instance, say you have a program that needs to do a calculation based on the length of your array. Here is an example where we multiply each number in our list by twice the length of the list.

This is without hoisting…

  1. var favorite_numbers = [1, 8, 1988, 4];
  2. for(var i=0; i<favorite_numbers.length; ++i) {
  3. var double_len = favorite_numbers * 2;
  4. var current = favorite_numbers[i];
  5. console.log(double_len * current);
  6. }

Notice here that double_len gets calculated to the same value for each iteration. This operation is unnecessary since our value is the same every time. We can reduce this iteration from i (the number of items in the list) to a constant 1 time by hoisting it like so…

  1. var favorite_numbers = [1, 8, 1988, 4];
  2. var double_len = favorite_numbers * 2;
  3. for(var i=0; i<favorite_numbers.length; ++i) {
  4. var current = favorite_numbers[i];
  5. console.log(double_len * current);
  6. }

Now, our calculation is done once instead of being recalculated for each iteration. As you can hopefully see, hoisting is effective here. However, the default behavior of the var declaration in the for loop is not necessarily useful for all purposes, sometimes it introduces strange behaviors like allowing variables to exist within scopes outside of their intended scope. This could cause your program to behave differently from what you originally expected, especially if have nested loops.

Rectifying Pitfalls and ECMA6

These behaviors are very unusual and it is also what makes JavaScript an unattractive language. Even seasoned programmers who dive into JavaScript without knowledge of these pitfalls will have a very difficult time figuring out why their code is behaving so strangely. To Rectify this, there have been several standards that attempt to address these issues. JavaScript is actually a derivative (a dialect) of ECMAScript. ECMAScript defines the complete set of standards for the language. JavaScript is a derivative of ECMAScript in that it derives a subset of ECMAScript’s features to work with HTML5 standards. Both ECMAScript and HTML5 are responsible for defining the standards for JavaScript.

The let keyword and ‘Use Strict’

There were several major issues with the var keyword because its default behavior is hoisting. This allowed for variables to live outside it’s local scope as we mentioned earlier, many strange behaviors arose such as:

● Variables are able to be assigned before declared

● Variables in for loops continue to live on after the termination of a loop

● Variables inside of if statements are hoisted outside to the parent scope

● Variable re-declaration can occur

These are major issues because your application is built to precisely modify and manipulate data, so you want your programming language to be very careful when handling variables. The programming language you are using should define exactly how variables are created and it’s accessibility; nothing weird or confusing. As such, these behaviors are dangerous, especially for those who are unaware of these pitfalls. Imagine writing a serious application that deals with transactions like money or bidding with consequences that are hard to reverse.

As an attempt to directly address these issues, JavaScript introduced the let keyword. The let keyword works more like a normal variable declaration keyword that you see in a normal programming language. It prevents all of the pitfalls associated with the var keyword mentioned above and ensures that the variable you declare live within the scope that you defined.

The ‘use strict’ statement placed at the top of your code document will prevent var from declaring your variables before being used and redeclaring your variables with var. This was an initial attempt to rectify the dangerous behavior of var.

Most of the unpredictability and headaches with the var can be avoided by simply by using let keyword instead. In principle, you will not need the ‘use strict’ statement if you’re using let instead of var, however, many people still make use of the statement at the top of their documents in case var is ever used.

So how exactly does the let keyword work?

Block bindings, the stuff within the curly braces {…}, such as the body of functions, if statements, and for loop now behave more appropriately like most C-based languages. Declaration of a variable using the let keyword will only allow for accessibility from within the block scope or inside child scopes, it is not be accessible from outside of the block binding.

The following shows the differences between var and let

var example

if(true) {
var i = 2;
}

console.log(i); //displays 2

let example

if(true) {

let j = 4;

}

console.log(j); //displays undefined

As expected, the var declaration hoists the variable outside of the scope of the body of the if statement so console.log displays the value of 2 even though it was defined within another scope. On the other hand, j was defined using the let keyword so it was not hoisted outside of the body of the if function so j is unaccessible outside of the body of the if function, it is thus undefined.

The following demonstrates how the let keyword allows child scopes still have access to their parent declared variables (again like most c-based languages)…

Scope bindings are exposed from parent to child.

for(let i = 0; i<400; ++i) {

if(true) {

console.log(i);

}

}

This is a trivial example that iterates from 0 to 399 and prints i on each iteration. When you execute the code you will notice that although i was declared in the parent scope, it is still accessible from the body of the if statement which is nested one layer deeper. Children scopes have access to their parent’s declarations.

Intorducing the const modifier

In addition to the let keyword, we also have the const keyword.This keyword works by preventing an assignment from happening twice. Constants, as their names imply, are supposed to be unchangeable after they are first assigned. As a matter of fact you must declare them and assign them in the same line.

To illustrate, say we have some predefined variables…

let i = -1;

let j = 2;

let k = 33;

the following will be acceptable

const a = 0;

const b = j; //j was defined earlier

const c = 1.22;

const d = a;

The following is not acceptable

a = i; // it was already declared

const e; // you must declare and assign it in one line

const a = 100; // it was already declared, you cannot redeclare it

This should be somewhat easy to understand, but C-based developers from languages like C++, Java, or C# will cringe a little when seeing this. Why? Because the const keyword in these languages imply compile-time constants; that means they are declared before the application even starts compiling so you cannot assign a constant to a variable. This allows compilers to predict and optimize applications more efficiently. However, since JavaScript is interpreted, it will execute code line-by-line during runtime rather than compiling everything together into a binary. In this manner, the const keyword acts more like a read-only property rather than a true constant. If you do not understand this distinction, don’t worry, it’s for people who come from other languages with the const keyword.

So why should you use the const keyword?

On purpose: when you are declaring something like pi or e and you don’t want it to be unintentionally changed by either yourself or somebody else. It is also used when you are declaring an object and you don’t intend for it to be modified like when declaring the this pointer as a viewmodel or when referencing to an object that you do not want to change.

Efficiency: the interpreter will look for opportunities to optimize your code. When the interpreter detects constants that are declared, it will try to find a way to optimize it. It is also usual for constants to be public or global, this way it is declared once and utilized throughout the application.

The var keyword is an aspect of JavaScript that is is a bit weird, but very important to know. Having this foundational understanding of JavaScript is important for writing solid applications. ECMA6 is still new compared to the bulk of the JavaScript code that has existed prior to the introduction of the let keyword. That means it is still pretty important to understand how var works because you may have to work with legacy code that uses var; also, since there is no intention to deprecate var yet, there is unfortunately still going to be some JavaScript developers that that will continue to use var so you may still bump into it again.

If you like this video, please subscribe to our FaceBook page at fb.com/intully channel or our channel in the description. If you want even more, subscribe to our newsletter at intully.com where we will announce early video releases as full length videos of all of our content as well as exclusive drills aimed at helping you better understand this content.

--

--