An Introduction To Scope In ECMAScript 6

by | Jul 31, 2015

6 (ES6) introduces some new features that improve upon ECMAScript 5 (ES5) scoping. In this post we’re going to look at arrow function scope, block scope, and two new ways to declare variables in ES6.

A solution to ‘var self = this’

A common situation in javascript is needing to access a property or method of the parent scope object within a closure. To solve this, we declare a variable referencing the parent scope, and then access the desired property or method using that variable within our closure. Consider this example:

class Pizza {
    constructor(name toppings) {
        this.name = name;
        this.toppings = toppings;

        this.availableMeats = ['Chicken', 'Pepperoni'];
    }
    
    getMeatToppings() {
        var self = this;
        
        return this.toppings.filter(function(topping){
            return (self.availableMeats.indexOf(topping) > -1);
        });
    }
}

Notice the var self = this in the Pizza.getMeatToppings method. ES6 introduces a way to declare functions that don’t create an isolate scope. You can declare a method using an arrow function instead of the function keyword:

class Pizza {
    constructor(name toppings) {
        this.name = name;
        this.toppings = toppings;
 
        this.availableMeats = ['Chicken', 'Pepperoni'];
    }
    
    getMeatToppings() {
        return this.toppings.filter((topping)=>;{
            return (this.availableMeats.indexOf(topping) > -1);
        });
    }
}

Notice the way Pizza.getMeatToppings() is declared with => instead of function. This new syntax is called an arrow function.

Block scope in ES6

Variables in ES5 can be in two scopes — function scope or global scope. ES6 introduces a new way to declare variables in block scope using the let keyword.

getData().then(function(response){
    if (response.status == 'OK') {
        var data = response.data;
       
       /* do stuff with data */
    }
});

In the example above, the data variable is often treated as if it its scope is the if block — when the variable is actually function scoped. The actual scope looks something like this:

getData().then(function(response){
    var data = undefined;

    if (response.status == 'OK') {
        data = response.data;
       
        /* do stuff with data */
    }
});

This is called hoisting. When a variable is declared, the declaration is ‘hoisted’ to the top of the current scope.

Declaring block scoped variables with let

ES6 introduces a new way to define variables using the let keyword instead of var. Variables declared with var are function scoped — and variables declared with let are block scoped. We can revise the above example to make the data variable scoped to the if block, by using the let keyword:

getData().then(function(response){
    if (response.status == 'OK') {
        let data = response.data;
       
        /* do stuff with data */
    }
});

In the revised code, the data variable is scoped to the if block. The let keyword can be used to bind variables to the scope of a for loop:

for (let i = 0; i < 10; i++) {
    console.log(i); // > 1, 2 ... 9, 10
}

console.log(i); // > undefined

Declaring constants with const

Another new keyword for variable declaration in ES6 is const. Variables declared with const represent a constant reference to a value. The value being referenced may changed, but the value cannot be reassigned. Here’s an example:

const languages = ['English', 'Spanish'];

data.push('French');

console.log(data); // > ['English', 'Spanish', 'French'];

The above example will print out the languages array, including the item we pushed. If we try to assign the constant to a new array, an error is thrown:

const languages = ['English', 'Spanish'];

languages = []; // -> Error

There’s a lot of confusion surrounding scope in javascript. With the advancements brought by ES6, scoping will become more manageable.

Recent Posts