Modern JavaScript (ES6+) features that browser can understand

Chirag Goel
7 min readMay 9, 2018

JavaScript has progressed a lot over the past few years. Whether you are new to JavaScript, or have some experience up your sleeves and want to quickly get up to speed with the most commonly used features that are natively supported in all modern browsers, then you are at right place.

If you haven’t touched ES6, you’re missing out on an easier way to read and write JavaScript.

When browser can understand ES6, then why can’t you.

Most commonly used ES6 features include:

  • Template literals
  • Let and Const Block scoping
  • Arrow Functions
  • Destructuring
  • Default parameters
  • Rest parameter and spread operator
  • Modules and Classes
  • Iterators and Generators
  • Promises etc

Out of these I’m going to dive into details of 4 common natively supported features by browser — Template literals, Let and Const Block scoping, Arrow Functions and Destructuring.

Template Literals

var name = 'Chirag'; 
var message = 'Hi ' + name + ',';

We can define a string with placeholders and get rid of all those concatenations. So, above code can be replaced by using enclosing strings within back-ticks (`).

var name = 'Chirag'; 
var message = `Hi ${name},`;

To add a placeholder in a template literal, we use the ${expression} syntax. You can replace “expression” with any JavaScript expressions. Here, we are adding the name variable there. You can also call a function or pass any JavaScript expressions that results in a value.

Another benefit of using template literals is that they can expand multiple lines. They are particularly useful when composing email messages:

var message = `
Dear Concatenation,

Thank you for being with us for so long. Want to say bye bye, as we have template literal now.

Happy coding,
${name}
`;

Let and Const

Prior to ES6, we used the var keyword to define variables. The scope of a variable defined using the var keyword is the entire enclosing function. Here’s an example:

function doSomething() {
for (var x = 0; x < 5; x++) {
/* Technically, x should only be scoped to this block because this is where we have defined x */
}

/* But it turns out that x is available here as well! */
console.log(x); // 5
}

That’s not how most if not all other programming languages behave! Variables defined within a block should be scoped to that block only. In this example, x should not be accessible outside of the for block.

Another issue with the var keyword is that if you use it at the top level outside of a function, it creates a property on the global object:

var x = 1; 
console.log(window.x); // 1

ES6 introduced 2 new keywords for resolving these issues: let and const. Both these keywords define variables that are scoped to the containing “block” not “function”:

function doSomething() {
for (let x = 0; x < 5; x++) {
/* With the "let" keyword, now x is only accessible in this block.*/
}

/* x is out of the scope here */
console.log(x); // x is not defined
}

With const we can define a constant. So we cannot reassign it later:

const x = 1; 
x = 2; // throws "Assignment to constant variable."

Also, unlike the var keyword, let and const don’t create a property on the global object if you use them at the top level:

let x = 1; 
console.log(window.x); // undefined

So, here is what you should take away:

  • var is function-scoped where as let and const are block-scoped.
  • Say bye bye to the var keyword and hello to let and const.
  • Prefer const to let. Use let only if you need to re-assign the identifier; otherwise, use const to prevent accidentally re-assigning a constant. Prefer const for function declaration.

Arrow Functions

Most loved one feature in ES6! Inspired by lambda expressions, arrow functions give you a clean and concise syntax for writing function expressions. Here’s a function expression in ES5:

//ES5
const square = function(number) {
return number * number;
}
//ES6
const square = (number) => {
return number * number;
}
//ES6 - if function has single line expression to return
const square = (number) => number * number;
//ES6 - if function has only one argument.
const square = number => number * number;
//ES5 - if function has no argument.
const sayHello = () => { console.log('hello'); };

Well, it turns out that we’re not just substituting function with =>. An arrow function’s syntax can change depending on two factors:

  1. The number of arguments required
  2. Whether you’d like an implicit return.

The first factor is the number of arguments supplied to the arrow function. If you only supply one argument, you can remove the parenthesis that surrounds the arguments. If no arguments are required, you can substitute the parenthesis (()) for an underscore (_).

The second factor for arrow functions is whether you’d like an implicit return. Arrow functions, by default, automatically create a return keyword if the code only takes up one line, and is not enclosed in a block.

Arrow notation can be used in callback as illustrated below:

/* Normal function in a callback */
button.addEventListener('click', function () {
// Do something
})
/* Arrow function in a callback */
button.addEventListener('click', () => {
// Do something
})

Prefer => notation in anonymous function and callbacks.

Using arrows functions in ES6 allows us to stop using that = this or self = this or _this = this or .bind(this).

this is a unique keyword whose value changes depending on how it is called. When it’s called outside of any function, this defaults to the Window object in the browser.

console.log(this) // Window

When this is called in a simple function call, this is set to the global object. In the case of browsers, this will always be Window.

function hello () {   
console.log(this)
}
hello() // Window

JavaScript always sets this to the window object within a simple function call. This explains why the this value within functions like setTimeout is always Window.

When this is called in an object method, this would be the object itself:

let you = {   
sayThis: function() {
console.log(this)
}
}
you.sayThis() // you

When the function is called as a constructor, this refers to the newly constructed object.

function Person (age) {   
this.age = age
}
let greg = new Person(22)
let thomas = new Person(24)
console.log(greg) // this.age = 22
console.log(thomas) // this.age = 24

When used in an event listener, this is set to the element that fired the event.

let button = document.querySelector('button')  button.addEventListener('click', function() {
console.log(this) // button
})

As you can see in the above situations, the value of this is set by the function that calls it. Every function defines it’s own this value.

In fat arrow functions, this never gets bound to a new value, no matter how the function is called. this will always be the same thisvalue as its surrounding code. (By the way, lexical means relating to, which I guess, is how the lexical this got its name).

First, you never want to use arrow functions to declare object methods, because you can’t reference the object with this anymore.

let o = {   
// Don't do this
notThis: () => {
console.log(this) // Window
this.objectThis() // Uncaught TypeError: this.objectThis is not a function
},
// Do this
objectThis: function () {
console.log(this) // o
}
// Or this, which is a new shorthand
objectThis2 () {
console.log(this) // o
}
}

Second, you may not want to use arrow functions to create event listeners because this no longer binds to the element you attached your event listener to.

However, you can always get the right this context with event.currentTarget. Which is why I said may not.

button.addEventListener('click', function () {
console.log(this) // button
})
button.addEventListener('click', e => {
console.log(this) // Window
console.log(event.currentTarget) // button
})

Third, you may want to use the lexical this in places where the thisbinding changes without you wanting it to. An example is the timeout function, so you never have to deal with the this, that or selfnonsense.

let o = {   // Old way
oldDoSthAfterThree: function () {
let that = this
setTimeout(function () {
console.log(this) // Window
console.log(that) // o
})
},
// Arrow function way
doSthAfterThree: function () {
setTimeout(() => {
console.log(this) // o
}, 3000)
}
}

Destructuring

Destructuring is a convenient way to get values out of arrays and objects. There are minor differences between destructuring array and objects, so let’s talk about them.

Destructuring objects:

const person = { 
name: 'Chirag Goel',
company: 'MoEngage',
city: 'Bangalore',
country: 'India'
};
const name = person.name // Chirag Goel
const company = person.company // MoEngage
const city = person.city // Bangalore
const country = person.country // India
//ES6
const { name, company, city, country} = person;

Nested object example:

const person = { 
name: 'Chirag Goel',
address: {
billing: {
street: '123 Flinders st',
city: 'Melbourne',
state: 'Victoria'
}
}
};
const street = person.address.billing.street;
const city = person.address.billing.city;
const state = person.address.billing.state;
//ES6
const { street, city, state } = person.address.billing;

Destructuring arrays:

Destructuring arrays and destructuring objects are similar. We use square brackets ([]) instead of curly brackets ({}).

When you destructure an array,

  • Your first variable is the first item in the array.
  • Your second variable is the second item in the array.
  • and so on…
let scores = ['98', '95', '93', '90', '87', '85'] 
let [first, second, third, ...rest] = scores
console.log(first) // 98
console.log(second) // 95
console.log(third) // 93
console.log(rest) // [90, 87, 85]

Wrapping up

Woo! That’s almost all the awesome ES6 features supported by browsers and I use on a regular basis. ES6 is awesome. I will be covering other ES6, ES7 and ES8 feature in my upcoming articles.

It’s definitely worth it to take a bit of your time and learn about them, so you can understand what everyone else is writing about.

Was this article helpful for you? Let me know in the comments below if you have any questions or thoughts! I’d love to hear them :)

Thanks for reading. Did this article help you in any way? If I did, I hope you consider sharing it you might just help someone who felt the same way you did before reading the article. Thank you.

Sharing makes you bigger than you are. The more you pour out, the more life will be able to pour in.

--

--

Chirag Goel

Senior Software Engineer @Microsoft | Blogger | Youtuber | Mentor | Entrepreneur. I’m the intersection of Technology, Business and Design.