Shreya

ES6 Handbook: The Complete Guide

Created September 20, 2021

Hey readers! This blog is all about ES6. It includes all the topics related with examples. Before reading further, I want to specify that this was not a blog post initially, these are just my personal notes that I use as a reference guide, so I apologize for any misspells here :)

Table of Contents

Notes

let/const

Before moving to the point, let us understand two concepts here:

  1. Global Scope - Variable is declared outside the function. This variable is accessible inside every function present in the code.
  2. Function Scope - Variable is declared inside (within) a function, outside that it is not accessible anywhere.
  3. Block Scope - In short, block scope means variables which are declared in a { } block are not accessible outside it. This block can be an if statement, for/while loop, etc.

var: function/ global scoped. Eg:

→ as you can see, var is both global and function scoped, which often creates a confusion. So avoid using it.

var name = 'Jack';              // global scope

function message() {
    var msg = 'Hey Jack!'       //function scope
}

console.log(msg);               // ERROR

The above line of code will throw an error as there's no variable msg outside the function message (where we have logged the variable). So it will show as undefined.

let: block scoped. Eg:

let keyword can't be redeclared:

let x = 1;
let x = 3;

result: SyntaxError - redeclaration of let x

But when we use let inside a function, it works like:

let size = "big";

function box() {
    for (let x = 0; x < 7; x++) {
        console.log(size);

        //Output: ReferenceError - `size` is not defined
        
        let size = "small";
        console.log(size);
    }
}

box();                          // small
console.log(size);              //big


Inside the function box() when we log the value of size, it shows a reference error. That is because, let is block scoped.

Anything inside curly braces { } is block scoped. In the above scenario, the function box() is a block.

const: block scoped. Eg:

const are very similar to let except that they can't be changed and redeclared.

const m = 8;
console.log(m);             //m = 8
	
m = 5;                     // 🚫 this will throw an error 
console.log(m);

// Uncaught TypeError: invalid assignment to const 'm'.
}

→ therefore let and const are preferred over var keyword for declaring variables.


Objects

key: property name

value: value of that property

const car = {
    model: 'Tesla',
    color: 'black',
    price: 800
}

Talking specifically about ES6, before ES6 we had to specify both (key, value) even if both are of same names.

function Boy(name, age) {
    return(
        name: name,
        age: age
    );
}

ES6 help us to get rid of duplication when we have same key:value names. So now our code will look like this:

function Boy(name, age) {
    return(name, age);
}

this

this is a keyword. It basically returns a reference to the object it is placed within

💡 NOTE:

const user = {
    name: 'Mike';
    call() {
        console.log(this);
    }
}
user.call();

// ⚙️ Output: {name: 'Mike, call: f} 

const user = {
    name: 'Mike';
    call() {
        console.log(this);
    }
}

const myCall = user.call;

myCall();

// ⚙️ Output: undefined

Arrow Functions

const square = function(num) {
    return num * num;
}
const square = num => num * num;

array.map()

If we have an array -

const colors = ["red", "green", "blue"];

We want to map the objects. Now there are two methods, es6 one is shorter and easier.

const items1 = colors.map(function (color) {
    return "<li>" + color + "</li>";
});
const items2 = colors.map((color) => `<li> ${color}  </li>`);


Object Destructuring

Let's say we have an object called girl such that it has 3 keys as follows:

const girl = {
    name: "",
    age: "",
    country: "",
};
const name = girl.name;
const age = girl.age;
const country = girl.country;

const { name, age, country } = girl;

this one line code works same as the previous code. So destructuring made our code shorter and easier to understand.

const {country: ctry} = girl;

This above line of code means we've defined a new variable called ctry and set that equals to country.


Spread Operator

CASE I - COMBINING ARRAYS

const one = [1, 2, 3];
const two = [4, 5, 6];

const combined = one.concat(two);

const combined = [...one, ...two];

const combined = [...one, '9', '7', ...two ];

const myDupli = [...two];

CASE II - COMBINING OBJECTS

const alpha = { name: 'Shreya' };
const beta = { age: 19 };

const combined = {...alpha, ...beta};

const gamma = { ...alpha, surName:'Purohit', ...beta, country: 'India'}

const betaV2 = {...beta};


Classes

const boy = {
    name: "Sam",
    run() {
        console.log("running...");
    },
};
class Boy {
    constructor(name) {
        this.name = name;
    }

    run() {
        console.log("running...");
    }
}

const boy = new Boy("Samridh");

with this above class, we've implemented the run method in a single line of code. If someday we find a bug here, we've to modify it in just a single place {inside class Boy}. So this is the advantage of using classes in JS.


Inheritance

class Boy {
    constructor(name) {
        this.name = name;
    }

    run() {
        console.log("running");
    }
}
class Girl extends Boy {
    eat() {
        console.log("eating");
    }
}

const myGirl = new Girl("Shreya");

myGirl.eat();
myGirl.run();
class Girl extends Boy {
    constructor(age) {
        this.age = age;
    }
    eat() {
        console.log("eating");
    }
}

// *result - Uncaught ReferenceError: must call super constructor before using 'this' in derived class constructor*
class Girl extends Boy {
    constructor(name, age) {
        super(name);
        this.age = age;
    }
    eat() {
        console.log("eating");
    }
}
const myGirl = new Girl("Shreya");

Modules

Sometimes we have many no. of classes declared in a single file. This makes the code long, confusing and messy. To avoid this, we separate these classes into different files and import them as a module into the main file. This is called modularity.

Let's look it in action. Here's what our folder src will look like:


// src/boy.js

export class Boy {
    constructor(name) {
        this.name = name;
    }

    run() {
        console.log("running");
    }
}


// src/girl.js

import { Boy } from './src/boy';

export class Girl extends Boy {
    constructor(name, age) {
        super(name);
        this.age = age;
    }
    eat() {
        console.log("eating");
    }
}

both Boy and Girl classes are private in the folder, in order to use them we made them public using the export keyword.

We use import keyword in line 1 of girl.js as it is an extended version of the Boy class.

Now half of the work is done. For now, these classes are not accessible in our main app.js file. For that we've to import them in our app.js file. We can do that by using -

import { Boy } from './src/boy';

import { Girl } from './src/girl';

Default and Named Exports

Named Exports

export class Car {
    constructor(model) {
        this.model = model;
    }
}

export function add(a, b){
    return a + b;
}

Default Exports

export default class Car {
    constructor(model) {
        this.model = model;
    }
}

💡 Now we don't need the import { Car } from "./car";

Instead, we use import Car from "./car"; in case of default exports.

Default exports -> import Car from "./car";
Named exports -> import { Car } from "./car";

👋 Woosh! You've made it to the end. Hope I've helped you somehow. I write articles like this whenever I've some spare time. Besides this, I share content related to web development daily on Twitter. Let's connect there! @eyeshreya