Skip to content

Modus-Logo-Long-BlackCreated with Sketch.

  • Services
  • Work
  • Blog
  • Resources

    OUR RESOURCES

    Innovation Podcast

    Explore transformative innovation with industry leaders.

    Guides & Playbooks

    Implement leading digital innovation with our strategic guides.

    Practical guide to building an effective AI strategy
  • Who we are

    Our story

    Learn about our values, vision, and commitment to client success.

    Open Source

    Discover how we contribute to and benefit from the global open source ecosystem.

    Careers

    Join our dynamic team and shape the future of digital transformation.

    How we built our unique culture
  • Let's talk
  • EN
  • FR

Quick Practical Guide for ES6: Part 2

Published on September 24, 2018
Last Updated on April 8, 2021
Application Development

This post is part 2 of the series on ES6. If you have skipped part 1, you can view it here. While the topics covered in this post are not related to the part 1, reading part 1 first is recommended. In this post, we will discuss more ES6 features that are really useful. If you’ve mastered these, you can jump into Angular 2+ and React Native development. Let’s start!

Fat Arrow Functions

If you are already using fat arrow functions in ES6, you know how easy and amazing they are. First, let’s take a look at a simple example using ES5:

Anonymous function: a function that does not have a name. For example:

setTimeout(function() {
    console.log('I am here after 3 light-years ;)');
}, 3000);

Let’s give this anonymous function a name:

function myFunction() {
    console.log('I am your function');
}
setTimeout(myFunction, 3000);

The above function has a name, myFunction. Notice the argument of the setTimeout function. We are passing a function as an argument, because JavaScript functions are first-class functions. This means, a function can take another function as its argument.

ES6 introduced fat arrow functions, which is a slightly shorter way of writing anonymous functions.

“An arrow function expression has a shorter syntax than a function expression and does not have its own this, arguments, super, or new.target. These function expressions are best suited for non-method functions, and they cannot be used as constructors.” ~ mozilla docs

Since arrow functions do not have their own this, the output is not similar as with function expressions. Example:

var compareObj= {
  age: 26,
  ageArrow: () => console.log(this.age, this),
  ageExpression: function() {
    console.log(this.age, this);
  }
}

 >   compareObj.ageArrow();
 //   undefined
Window      
     
>  compareObj.ageExpression();
// 26 {age: 26, ageArrow: Æ’, ageExpression: Æ’} 

The above setTimeout anonymous function could also be written using fat arrow function:

Example 1:

setTimeout(() => {
     console.log("I am here after 3 light-years ;)");
}, 3000);

Example 2: Simple sum function legacy declaration and fat arrow representation.

const sumFun = function(arg1, arg2, arg3) {
    return arg1 + arg2 + arg3;
};

Fat arrow representation:

const sumFun = ( arg1, arg2, arg3 ) =>  arg1 + arg2 + arg3;

Or: in case of multiline statements inside function: you are required to return explicitly by using the return keyword, like in the following example. If it is one line statement, it is automatically returned (as stated in above example).

const sumFun = ( arg1, arg2, arg3 ) =>  {
    return arg1 + arg2 + arg3;
}

Destructuring

Destructuring is a way to extract the values from an array and an object directly into variables. Let’s understand by taking an example:

const modusEmployee = {
    name: ‘Nikhil’,
    country: ‘India’,
    city: ‘New Delhi’,
    nativeLanguage: ‘Hindi’
};

In ES5, we could do the object value extracting as:

const empName = modusEmployee.name;
// Nikhil
const empCity = modusEmployee.city;
// New Delhi

In ES6, it is improved:

const { name, city } = modusEmployee;
console.log(name);
	// Nikhil
console.log(city);
	// New Delhi

Here we are using alias for property name

const { name: empName, city: empCity } = modusEmployee;
console.log(empName);
// Nikhil
console.log(empCity);
// New Delhi	

Destructuring can be used in the following ways:
Object destructuring
Array destructuring
Function parameter destructuring

We have already covered the object destructuring, let’s see how Arrays destructuring works.

Array destructuring is quite similar to object destructuring. The difference is that it extracts the values based on index.

const myCoins = [ 1,2,5,10 ];
const [ coin1, coin2 ] = myCoins;
console.log(coin1)
// 1
console.log(coin2);
// 2

Bonus points!
If you just want the third value:

const [ , , coin3 ] = myCoins;
console.log(coin3);
// 5;

If you want all values in one variable:

const [ ...all ] = myCoins;
console.log(all);
 // [1,2,5,10]

If you want to get rest of the values:

const myCoins = [ 1,2,5,10 ];
const [ coin1, ...otherCoins ] = myCoins;
console.log(coin1);
// 1
console.log(otherCoins);
// [ 2, 5, 10 ]

Handling conditions when no values are available for destructuring: Default Values. You can provide default values when the property you are destructuring is not defined:

const [ random = ‘not found’ ] = [ ‘Available’ ];
	console.log(random);
// Available

const  [ random = ‘not found’ ] = [];
	console.log(random);
	// not found

Object Orientation: Class

JavaScript is a prototype-based programming language and in ES5 there was no class keyword available to use. It does not mean that we couldn’t code using object oriented programming in ES5. We can mimic object oriented programming in ES5 by creating an object with prototype methods. Inheritance can also be achieved by using these objects. This is how we have had to create a “class” with ES5:

var Automobile = function (engineSound) {
    this.engineSound = engineSound;
} 

Automobile.prototype.engineSoundFun = function() {
    console.log(this.engineSound)
};

var enfield = new Automobile(‘dhuk dhuk’);

enfield.engineSoundFun();

Output:
// dhuk dhuk

If you are familiar with C++ or Java then you might be thinking, “Where is object orientation in the above example since no class keyword is used?” The syntax became easier to understand in ES6 as the classical object oriented pattern in other languages. ES6 introduces the class keyword that makes it much easier to understand. ES6 Classes support prototype-based inheritance, super calls, instance and static methods, and constructors.

Let’s do the above example in ES6 way:

class Automobile {
    constructor(engineSound) {
        this.engineSound = engineSound;
    }

    engineSoundFun() {
        console.log(this.engineSound);
    }
}

const enfield = new Automobile(‘Dhuk Dhuk’);
	
enfield.engineSoundFun()

Output:
// Dhuk Dhuk

Notice the code is much cleaner, easier and familiar in ES6. However, the prototype pattern is still available to use in ES6.

Template Literals

Remember, debugging in code using console.log and variable concatenation:

var name = ' Welcome! ' + first + ' ' + last + '.';
console.log(‘Hi ‘ + name + ‘ your username is ‘ + username):

Luckily, in ES6 we can use a new syntax ${first} inside of the back-ticked string in order to clean it up:

const name = ` Welcome! ${first} ${last}.`;
console.log(`Hi ${name} your username is: ${username}`);

Another issue before was using multiline strings, the new line statements used were printing using special characters like \n:

var message = ‘Individuals in semler project \n are not just developers or testers, they are \n good friends’;

Using a template literal with the back-tick now works just as you would expect it to:

const message = `Individuals in semler project are not just developers or testers, they are
good friends`;

Spread Operator

If you are aware of the concept of deep copy and shallow copy in JavaScript (take a glimpse below in the bonus section of this post), you can understand that sometimes it becomes hard to analyze what is happening between objects copying and their usage.

Code speaks more, let’s understand the scenario by example (without spread operator):

const myCurrencyNotes = [1, 5, 10, 1000]; 
const piratedNotes = myCurrencyNotes;

console.log(piratedNotes);

Output:

 [1, 5, 10, 1000]

> piratedNotes.push(50);

console.log(piratedNotes);

Output:
[1, 5, 10, 1000, 50]

Now, when you log:

> console.log(myCurrencyNotes);

Output:
[1, 5, 10, 1000, 50]

We know that when working with objects in JavaScript (arrays are a type of object) we assign by reference, not by value.

Same case with the usage of spread operator:

const myCurrencyNotes = [1, 5, 10, 1000];
const piratedNotes = [ ...myCurrencyNotes ];

console.log(piratedNotes);

Output:
[1, 5, 10, 1000]

> piratedNotes.push[50,100]

> console.log(myCurrencyNotes);

Output:
 [1, 5, 10, 1000]

Spot the difference here, when we push elements in piratedNotes array, the original array myCurrencyNotes is not affected.

Another use case of spread would be:

var newCurrencyNotes = [ 50,100 ];
var myAllCurrencyNotes = [ 1, 5, 10, newCurrencyNotes, 1000 ];

console.log(myAllCurrencyNotes);

Output:

[1, 5, 10, Array(2), 1000].

0:1
1:5
2:10
3:(2) [50, 100]
4:1000

You may not be expecting the 3rd step in output because it becomes a nested array, to eliminate such problem we can utilize spread operator:

const myAllCurrencyNotes = [1, 5, 10, ...newCurrencyNotes, 1000]
console.log(myAllCurrencyNotes);

Output:

[1, 5, 10, 50, 100, 1000]

Spread in function:

function checkFakeCurrencyNote(...notesVariations) {
    if (notesVariations.includes(1000)) {
        return ‘Contains an invalid currency denomination’;
    } else{
        return ‘Aha! nice currency’;
    }
}

const currency = [ 1, 10, 1000 ];
	> checkFakeCurrencyNote(...currency);
			
Output:
	"Contains an invalid currency denomination"

Promise

Objects are everywhere in JavaScript. A promise is also an object that returns its value in the future. Using a promise is useful when you have an asynchronous operation to perform. A promise can either be resolved or rejected.

A promise in JavaScript is similar to our real-life promise. Suppose you invited your friend to a party (request sent) and he told you he may organize the party (resolve) or he may not (rejected). If a promise is called resolved and you can process it further using .then(). If the promise is rejected and it can be handled in a .catch() or as the second argument in a .then().

You can make a promise by using new Promise. This Promise constructor takes in a function that contains two arguments — resolve and reject. The basic skeleton for creating a promise is:

new Promise((resolve, reject) => {
    /* Do something here */
});

The promise is created with name promise, as we have already discussed above, the case of .then() and error:

promise.then((resolvedValue) => {
    console.log(resolvedValue);
}, (error) => {
    console.log(error);
});

Let’s understand by taking a complete snippet:

const yourAge = window.prompt(‘Enter your age’, 13);
console.log(`Entered age is ${yourAge}`);

The variable yourAge is declared and ready to store the user’s random input value in a browser prompt. We will use this random value later in the program:

new Promise((resolve, reject) => {
    If ( yourAge < 99  ) {
        resolve("Congrats!! you are still young Carlos");
    } else {
        reject(new Error(‘Now at this stage, you should start eating tons of veggies’));
    }
});

Here, I have initialized a promise using new Promise and passed two arguments. The nested logic compares the variable yourAge value. If the value is less than 99, the promise returns resolve, else it rejects and outputs with an error.

If we simply run the above snippet with positive case value i.e value < 99, we will get nothing as output and if we provide negative case value i.e value > 99, we will get an Uncaught error:

Uncaught (in promise) Error:  Now at this stage, you should start  eating tons of veggies
    at Promise (:5:13)
    at new Promise ()
    at :1:17

Note: Though you can remove new Error from reject and simply return a string like resolve, it is preferred to reject with an error but is not required.

reject(‘Now at this stage, you should start eating tons of veggies’);

But if your input is a negative case value, you will still get:

Uncaught (in promise)
Now at this stage, you should start eating tons of veggies

These responses are expected. If you see the previous example above, we have to use .then and .catch to get the expected responses.

promise.then((resolvedValue) => {
    console.log(resolvedValue);
}, (error) => {
    console.log(error);
});

Suppose you provided 89 as an input value, the output will be:

Congrats!! you are still young Carlos

Promise {: undefined}
__proto__: Promise
[[PromiseStatus]]: "resolved"
[[PromiseValue]]: undefined

And for negative case input, suppose you provide 120:

Now at this stage, you should start  eating tons of veggies

Promise {: undefined}
__proto__:Promise
[[PromiseStatus]]:"resolved"
[[PromiseValue]]:undefined

Bonus: Shallow and Deep Copy in JavaScript

Shallow copy means the memory location of both objects will be the same. So if you change the property value of the copy object, the property value of the original object will also change at deep level of object. A shallow copy will duplicate the top-level properties, but the nested object is shared between the original (source) and the copy (target). But in deep copy, the memory locations will be different/separated. Changing the property values of copies object will not change the original one.

Shallow copy example: The Object.assign() method is used to copy the values of properties from one or more source objects to a target object:

let oldObj = {
    id:1
}

> oldObj
  {id: 1}
				
let newObj = { ...oldObj };

> newObj
  {id: 1}

> newObj.id = 9;
 
> newObj
   {id: 9}

> oldObj
    {id: 1}

Everything is fine until here, but as per the statement “A shallow copy will duplicate the top-level properties.” Let’s understand by an example:

let oldObj = {
  a: 1,
  b: {
    c: 2,
  },
}
let newObj = Object.assign({}, oldObj );
>newObj
 { a: 1, b: { c: 2} }

oldObj.a = 10;

> oldObj 
// { a: 10, b: { c: 2} }

>newObj 
// { a: 1, b: { c: 2} }

newObj.a = 20;
>oldObj
 // { a: 10, b: { c: 2} }

> newObj
 // { a: 20, b: { c: 2} }

newObj.b.c = 30;
> oldObj
 // { a: 10, b: { c: 30} }

> newObj
// { a: 20, b: { c: 30} }

To solve this problem, we have deep copy, check below. Thanks to my colleague, Mitchell @LikelyMitch, for simplifying this for me. Deep copy example:

const oldObj = {
    id: 1,
    foo: {
        bar: 8
    }
};

> const newObj  = JSON.parse(JSON.stringify(oldObj));
> newObj.foo.bar = 10
// 10

> oldObj.foo.bar;
// 8

What’s Next

I will update the link here of my upcoming post. There we will learn how users can access your application even when they have low network connectivity or even no network. I will share how your web application can load within 3 seconds (which is a performance benchmark criterion for a web application). I will also discuss how your web application can be used on a mobile device, like a hybrid mobile app with an app icon. So stay tuned, there’s more to come.

To gain a good understanding of ES6, I would recommend reading the first part of this topic.

I hope this was a good read. Please share your feedback & suggestions in the comments section.

Happy Coding!

Posted in Application Development
Share this

Nikhil Kumar

While working at Modus Create, Nikhil Kumar filled the role of Front End Engineer.
Follow

Related Posts

  • Quick practical guide for ES6 Featured Image
    Quick Practical Guide for ES6

    In this post we are going to examine what new features arrived in ES6 and…

  • ES6-Import-Statement-Without-Relative-Paths-Using-Webpack
    ES6 Import Statement Without Relative Paths Using Webpack

    The ES2015 module system is probably familiar to you by now. If you’ve used it,…

Want more insights to fuel your innovation efforts?

Sign up to receive our monthly newsletter and exclusive content about digital transformation and product development.

What we do

Our services
AI and data
Product development
Design and UX
IT modernization
Platform and MLOps
Developer experience
Security

Our partners
Atlassian
AWS
GitHub
Other partners

Who we are

Our story
Careers
Open source

Our work

Our case studies

Our resources

Blog
Innovation podcast
Guides & playbooks

Connect with us

Get monthly insights on AI adoption

© 2025 Modus Create, LLC

Privacy PolicySitemap
Scroll To Top
  • Services
  • Work
  • Blog
  • Resources
    • Innovation Podcast
    • Guides & Playbooks
  • Who we are
    • Our story
    • Careers
  • Let’s talk
  • EN
  • FR