Seven instances where ES6 makes your programming life easier
Tags: ES6

Seven instances where ES6 makes your programming life easier

ES6 isn’t new. In fact, since its inception in 2015, at least five other new versions have been released and with those, we have got a whole lot of cool new features at our disposal. But what makes ES6 still stand out among the rest of the versions is it changed the course of scripting language by introducing some of the path breaking features in scripting history.

The features include let and const, arrow functions, default parameters, rest and spread operators, classes, modules, promises, template literals, destructuring and much more.

But in this article, we will try to explore just seven instances where ES6 features come in handy and make your programming life easier.

let vs var:

In my opinion, developers with a C#/Java background hesitated a lot to pick up JavaScript just because of its outright weirdness. The scoping of var along with its most confusing hoisting problems made new learners to think JavaScript is extremely daunting. ES6 solved these issues in one shot with the introduction of let and const.

The main difference is var has functional level scope while let has block level scope and cannot be used before declaration, which essentially eliminates the hoisting problem. Let’s see why block scoping is important with the help of an example.

for (var i = 1; i < 5; i++) { console.log(i); setTimeout(function () { console.log(i); }, 1000) };

If you run the above code, you’ll end up with the most bizarre output with ‘i’ getting printed as ‘5’ four times in the console like how it is shown below.

Why? Well, the keyword var is the culprit here. Since var is function scoped and ‘i’ gets incremented to 4 even before the setTimeout runs, we get this weird output. Could it get any more confusing?

Okay, how to solve this then? Well, although defining a separate function for the setTimeout is enough do the job, there is a much better elegant solution which is to simply use block-scoped let. Using let, this is what we get as the output, which is what we intended in the first place.

So, when you declare variables, always prefer let over var wherever possible because it only makes the code less confusing and more friendly.

Template Literals:

Let’s be honest. Haven’t we ever felt annoyed when we try to handle strings in JavaScript, especially with multiple lines? What about your experience when you try to create HTML elements through JavaScript? Annoying at times, isn’t it? Let’s see why.

const name = “Raj”; const age = 23; const city = “Chennai”; // Traditional way const str = name + “,” + age + “lives in” + city;

Now, let’s try the same with template literals. To do this, ES6 provides you an option to enclose strings with backticks (`) and within that, you can gain access to usual JavaScript variables/properties using ${}.

// with backticks const str = `${name},${age} lives in ${city}`;

You see how simple is that? That’s how elegant template literal makes your code. Template literal is particularly powerful when you have to use multiple string or create HTML elements like below.

const div = document.createElement(‘div’); const list = `<ul> <li>one</li> <li>two</li> <li>three</li> <li>four</li> </ul>` div.innerHTML = list; document.body.append(div);

Get it? It’s as if you are using normal English. All you need is your backticks to make this magic work.

Arrow functions:

Arrow function is my favorite of the bunch. It is the one of the most important features of ES6 that enables us to create functions without the function keyword. Not just that, it also reduces the keyword return in some cases, making the code more concise and clearer. Let’s look at an example to see the striking difference.

let numbers = [1,2,3]; // Traditional way let squareNum = numbers.map(function(num){ return num*num; }); // ES6 way with Arrow function let squareNum = numbers.map(num => num*num);

In the above snippet, you can clearly see how concise the code is when you use the fat arrow function. The keyword function is gone, the explicit return is gone too, along with the parenthesis for the argument, thus promising a shorter syntax than the traditional way. Cool, isn’t it? But keep in mind, you still need to use the return keyword when you have multiple lines of code in your code block. Also, in case it is multiple argument function, you must use the parenthesis around the arguments.

Before you get too excited and start using arrow functions, there is one more thing to be aware of. Arrow functions do not bind their own this. Rather, they inherit it from the parent scope, which is what we call lexical scoping. To understand this, we must first need to know what this means in JavaScript.

The this in JavaScript is a unique keyword whose value changes depending on how it is called. For instance, when this is outside of a function, this refers to the Window object. When the same this is inside an object method, it refers to the Object itself.

function simpleFnc () { console.log(this); } simpleFnc() // Window let simpleObj = { simpleFnc: function() { console.log(this); } } simpleObj.simpleFnc() // simpleObj

But in arrow function, this will always be the same this value as its surrounding code. Let’s tweak the above example with an arrow function and see what we get.

let simpleObj = { simpleFnc: function() { console.log(this); } } simpleObj.simpleFnc() // simpleObj let simpleObj = { simpleFnc: () => { console.log(this); } } simpleObj.simpleFnc() // window

The above example clearly tells how arrow functions don’t bind their own scope but inherit from the parent, which in this case is Window object.

Arrow functions may seem a little hard to use at first, but once you get used to it, you’ll see a massive difference in your coding style.

Default parameters

As the name says, default parameters in ES6 allows us to provide default values to the parameters. Let’s straight away dive into an example to understand what exactly it means. Let’s say we have a function to display the project name to which the employee is currently assigned, like the following.

function getEmployeeStatus (name, project) { console.log(name + “, “ + project ); } getEmployeeStatus(“Vish”,”KLA”); //Vish, KLA

The above code works fine, sure. But what if an employee is not assigned to any project? In that case, the project becomes undefined, and if the argument project is undefined, how can the above code snippet handle the situation? It simply displays undefined in place of project, which is the last thing we want in such situation. To handle this in ES5, we can do the following.

function getEmployeeStatus (name, project) { if(project == undefined) { project = “Unassigned”; } console.log(name + “, “ + project ); } getEmployeeStatus(“Vish”); //Vish, Unassigned

Although we can pass unassigned as project, the better way to handle the situation is to check if the project is undefined. Problem solved, yes, but this is where ES6 gives an elegant solution. With default parameters, all we need to do is to assign a default value to the parameter and with that, JavaScript automatically assigns the default value whenever the parameter is undefined.

function getEmployeeStatus (name, project = “Unassigned”) { console.log(name + “, “ + project ); } getEmployeeStatus(“Vish”); //Vish, Unassigned

Rest and Spread Operators

If you happen to see a code with triple dots (…), it’s nothing but ES6 Rest and Spread operators. They look the same, i.e., tripe dots (…), but they differ a bit depending on what you use that for. Let’s take a look at Rest operators first. Consider the below example.

let title = “List of messages.”; let displayMessages = function() { console.log(title); for(let i in arguments) { console.log(arguments[i]); } } displayMessages(title,”Hi”,”Hello”,”How are you?”); // List of messages. // List of messages. // Hi // Hello // How are you?

Let’s say we have a function displayMessages that takes title as one argument and then a set of unknown number of arguments. To display the unknown arguments, we have to use arguments object to figure out the actual arguments. But there is a problem with this approach. In the above example, we want to display the title only once and the rest of the messages to be printed below it. But since the title is also a part of the arguments object, the title gets displayed again along with the rest of the arguments. Here’s where rest operator comes in handy. Let’s see how.

let title = “List of messages.”; let displayMessages = function(title, …msgs) { console.log(title); msgs.forEach(ele=>{console.log(ele)}); } displayMessages(title,”Hi”,”Hello”,”How are you?”); // List of messages. // Hi // Hello // How are you?

See how the rest operator packs the rest of the arguments into an array and displays it how we expected. That’s exactly the use of rest operator. Now, let’s move on to spread operator.

Spread operator does the opposite. While the rest operator packs everything into an array, the spread operator takes an array and spreads it into comma separated items. An example could explain it easily.

let array = [‘hi’, ‘hello’]; console.log(…array); // hi hello

See, that’s it. So simple and yet so useful in certain scenarios like array concatenation. In ES 5, if you have to concatenate arrays, you might have to use Array.concat method like below.

let array1 = [“One”,”Two”]; let array2 = [“Three”,”Four”]; let resultArray = array1.concat(array2); console.log(resultArray); // [“One”,”Two”,”Three”,”Four”]

But with spread operator all you have to do is this.

let resultArray = […array1, …array2]; console.log(resultArray); // [“One”,”Two”,”Three”,”Four”]

Destructuring

Destructuring is a handy feature that allows us to extract values out of arrays and objects. We’ll see one by one with appropriate examples and dig deep into them.

With Objects:

let person = { fname: “Vish”, lname: “S” } let fname = person.fname; //Vish let lname = person.lname //S

Let’s say we have an object person. In ES5, to get the value out of it and assign them to different variables, we do something like in the example, which is alright. But with destructuring, we can do the same in a single line, thus making the code much simpler.

let person = { fname: “Vish”, lname: “S” } let {fname, lname} = person; console.log(fname); //Vish console.log(lname); //S

But keep in mind you can’t declare a variable with the name already used. It will throw out an error. To handle this problem, we can always rename variables using a colon, like below.

let {fname, lname: surname} = person; console.log(fname); //Vish console.log(surname); //S

One last thing to address before we move on to destructuring arrays. If you try to destructure a variable that’s not in an object, it will only return undefined. But fortunately, we have a solution to handle this situation as well by leveraging default parameters feature.

let person = { fname: “Vish” } let {fname, lname: surname = “S”} = person; console.log(fname); //Vish console.log(surname); //S

With Array:

Destructuring Arrays is pretty much the same, only that it pulls apart the elements of the Array instead of an Object. Also, here, we use square brackets to destructure the arrays instead of curly brackets. Let’s look at an example.

let person = [“Male”,”Vish”,”S”]; let [gender,fname,lname] = person; console.log(gender); // Male console.log(fname); // Vish console.log(lname); // S

By using destructuring in the above code, the variables gender, fname and lname get mapped to the 1st, 2nd and 3rd elements of the array respectively with ease, thus saving us from writing considerable lines of code. Also, keep in mind, if an extra variable is present that can’t get mapped to any element in the array, it simply becomes undefined.

let person = [“Male”,”Vish”]; let [gender,fname,lname] = person; console.log(gender); // Male console.log(fname); // Vish console.log(lname); // undefined

In addition to that, it’s not always necessary to map every element to the array. To skip, however, the commas are necessary.

let person = [“Male”,”Vish”,”S”]; let [,,lname] = person; console.log(lname); // S

This is cool, isn’t it? But the best part is, just like in Destructuring objects, we can leverage features like default parameters and rest operator here in destructuring arrays.

// using default parameters let person = [“Male”,”Vish”]; let [gender,fname,lname=”S”] = person; console.log(gender); // Male console.log(fname); // Vish console.log(lname); // S // using rest operator let person = [“Male”,”Vish”,”S”]; let [gender,…name] = person; console.log(gender); // Male console.log(name); // [“Vish”,”S”]

Promise

Promise is one of the major and important features that came out as part of ES6. Why is it so? And what exactly is it? A promise is an object that may produce a single value in the future. Up until ES5, to handle asynchronous operations in JavaScript, you need to rely only on callback functions which is okay, but when it comes to nested calls, it is a pain to code. That’s where Promise comes in and saves us. Before we get to know why, we need to know how a Promise works. All you need to know is this. When the promise resolves, we consider the call as success and do something with the data that comes back with .then(). And when the promise rejects, we do the error handling with .catch().

Now, to the example. Let’s assume we have three asynchronous tasks in hand to be done in order. With plain callback functions, this is what we’ll end up.

doTask1((err, success) => { if (err) throw err doTask2((err, document) => { if (err) throw err doTask3(customer, (err, result) => { if (err) throw err console.log(‘All Tasks Done’); }) }) })

Now, with Promises, we can eliminate all these nested code and do something like below.

doTask1() .then(_ => doTask2()) .then(_ => doTask3()) .then(console.log(All tasks Done’)) .catch(err => console.log(err))

See how easy and clean to write asynchronous operations with Promises. It not only reduces the amount of nested code, but it also helps us to visualize the code flow and lets you handle all errors at the end of the chain in one shot.

Wrapping up

Okay, focusing just seven features out of a lot is cruel, I know, but my intention is not to introduce the new features that came out as part of ES6. After all, it came out way back in 2015 and you can find online a ton lot of other blog posts and articles which explain every single feature of ES6 in detail. Rather, my intention is to stress — yet again — how important ES6 was to JavaScript world when it came out and how this version stood out and made our lives easier.

These features are what make ES6 different from the other versions. They not only make the language easier for the developer to work, but also help to create a cleaner and concise codebase. So, if you haven’t used these features yet, consider checking them all out and I am sure you’ll find the difference in your everyday coding life.