Block-Level Scoping in ES 2015

BlockLevelScoping_ES2015

Introduction:

Scoping in JavaScript works little different compared to other languages. Without knowing this, many new and experienced developers have fallen prey to Scoping System in JavaScript. This is because of our coding habits in C#, Java or C++.

The Problem:

We usually create variables in for loop or if condition in a function and think that it will not be accessible outside that loop or if block.

Let us see this example, we are creating a variable ‘name’ in if-condition and try to print it in console outside the if-block. One may think that it will output undefined. But to our surprise, the value that is set in if condition will be printed.

function testMethod() {
     if(true) {
           var name = 'Pranav';
           console.log(name); // outputs 'Pranav'
     }
     console.log(name);  // will output 'Pranav'
}

The reason for this behaviour is that unlike other languages JavaScript follows Function-level variable ScopingJavaScript hoists variables to the top of the function no matter where they were declared in the function enforcing a function-level variable scoping.

This behaviour is evil at times and cause potential problems in large scale apps.

To solve this problem, ECMA Script 2015 ( ES 2015) introduces block-level scoping using two new keywords: let and const.

The Solution: Block-level Scoping using let and const

Both let and const create variables that are block-scoped – they only exist within the innermost block that surrounds them i.e., if-blocks, loop-blocks, anything with curly braces and naked curly braces too.

let:

Let us see an example to make it more clear.

function testMethod() {
     let name = 'Ainavolu';
     if(true) {
           let name = 'Pranav';
           console.log(name); // outputs 'Pranav'
     }
     console.log(name);  // outputs 'Ainavolu'
}

Here the scope of variable name in if-condition is limited to that block. It shadows the function-level variable in that context.

Another good thing with let and const is that they have temporal dead zone (TDZ). When entering its scope, it can’t be accessed (get or set) until execution reaches the declaration.

function testMethod() {
     name = 'Ainavolu'; // uninitialized binding - reference error
     console.log(name); // reference error

     let name; // TDZ ends; name is initialized with undefined
     console.log(name);  // outputs 'undefined'
     
     name = 'Pranav';
     console.log(name); // outputs 'Pranav'
}

const: 

const creates immutable variables i.e., variables whose value cannot be changed.

     const FILE_NAME = 'MyTestLogFile.log';
     FILE_NAME = 'AnotherLogFile.log'; // throws TypeError

Variables created with let are mutable. The value of those variables can be changed any time after declaration unlike const.

Here is an interesting case of const covered by Axel Rauschmayer.

    const obj = {};
    obj.name = 'Pranav';
    console.log(obj.name); // Pranav
    
    obj = {}; // TypeError

const does not affect whether the value of a constant itself is mutable or not: If a constant refers to an object, it will always refer to that object, but the object itself can still be changed (if it is mutable).

To make an object mutable, we need to use its freeze property.

const obj = Object.freeze();
obj.name = 'Pranav'; // TypeError

Recommended reading: Variables and scoping in ECMAScript 6 by Axel Rauschmayer