JavaScript has progressed a lot over the past few years. Whether you are new to JavaScript or have some experience and want to quickly get up to speed with the most commonly used features in modern JavaScript, this post is for you.
In this article, I’m going to share with you the features I use on a daily basis. These features include:
let
and const
This is not a comprehensive list of all the new features since ECMAScript 2015 (ES6). But these are the 20% of features that you use 80% of the time. Plus, this article is a work in progress. I’ll be adding more features to this list. For now, I want to publish this today!
All these new features are natively supported in all modern browsers. If you want to try out any of the code snippets below, simply open up Chrome Developer Tools and type the code.
So, let’s get started!
Before ES6, we had to deal with these ugly string concatenations:
var name = 'Mosh';
var message = 'Hi ' + name + ',';
Now, with template literals (previously called template strings), we can define a string with placeholders and get rid of all those concatenations:
var name = 'Mosh';
var message = `Hi ${name},`;
Note that I’m using the backtick character here. As funny as it sounds, nobody knew what backticks were until Angular 2 was released! That’s the character before number 1 on your keyboard.
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 result 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 = `
Hi ${name},
Thank you for joining my mailing list.
Happy coding,
Mosh
`;
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:
Ditch the
var
keyword. Use only 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.My favorite feature in ES6! Inspired by lambda expressions in C#, arrow functions give you a clean and concise syntax for writing function expressions. Here’s a function expression in ES5:
const square = function(number) {
return number * number;
}
With arrow functions, we get rid of the
function
keyword and put a fat arrow =>
between the parameters and the body of the function:const square = (number) => {
return number * number;
}
If our function is a one-liner and returns a value, we can drop the
return
keyword as well as the curly braces:const square = (number) => number * number;
Isn’t that much cleaner and more concise than the former syntax?
But wait, we can make this even shorter: If our arrow function includes only a single parameter, we can even drop the parenthesis:
const square = number => number * number;
What if our arrow function doesn’t have any parameters? We need to use a pair of parenthesis:
const sayHello = () => { console.log('hello'); };
Arrow functions are particularly useful when you need to pass callback functions as arguments:
// ES5
var activeJobs = jobs.filter(function(job) {
return job.isActive;
});
// ES6
const activeJobs = jobs.filter(job => job.isActive);
Arrow functions, unlike normal functions, don’t rebind
this
. Does this pattern look familiar to you?// ES5
function onSubmit() {
// Keep a reference to "this" so we can use it in the inner function below.
var that = this;
orderService.store(order, function(result) {
// In JavaScript, ever function defines its own "this" value. So, "this" in this inner function
// here is different from "this" in the onSubmit() function. That's why we had to keep a
// reference to "this" and store it in "that". Now, we can use "that":
that.result = result;
});
}
Arrow functions, unlike normal functions, don’t rebind
this
. They use the this
value of the enclosing execution context. So, if we replace the inner function above with an arrow function, we don’t need to keep a reference to this
anymore.// ES6
function onSubmit() {
orderService.store(order, result => {
// Since we're using an arrow function here, "this" references the "this" value of the containing function
// (onSubmit). Arrow functions don't re-define "this".
this.result = result;
});
}
Destructuring is an expression that allows us to extract properties from an object, or items from an array. Let’s say we have an address object like this:
const address = {
street: '123 Flinders st',
city: 'Melbourne',
state: 'Victoria'
};
Now, somewhere else we need to access these properties and store their values in a bunch of variables:
const street = address.street;
const city = address.city;
const state = address.state;
We have this repetitive code over and over: “address.” repeated 3 times. Object destructuring gives us a short and clean syntax to extract the value of multiple properties in an object:
const { street, city, state } = address;
That’s it! This code is exactly equivalent to the snippet above. We use curly braces on the left to destructure the address object. Inside the braces, we’re defining 3 variables: street, city, and state. Their values will be extracted from the corresponding properties in the address object.
Note that we don’t have to list all the properties in the address object. Maybe we’re interested only in the street property:
const { street } = address;
Object destructuring is particularly useful when you’re dealing with nested objects:
const person = {
name: 'Mosh',
address: {
billing: {
street: '123 Flinders st',
city: 'Melbourne',
state: 'Victoria'
}
}
};
Without destructuring, we would have to write this ugly and repetitive code:
const street = person.address.billing.street;
const city = person.address.billing.city;
const state = person.address.billing.state;
// So annoying!
Now, we can achieve the same result using a single line of code:
const { street, city, state } = person.address.billing;
We can also destructure arrays but we use square brackets ([]) instead of curly braces ({}). Let’s say we have an array and we want to extra the first and second item and store them in two different variables:
// ES5
const values = ['John', 'Smith'];
const first = values[0];
const last = values[1];
// ugly!
With destructuring, we can re-write the above code like this:
// ES6
const values = ['John', 'Smith'];
const [first, last] = values;
If you are looking to learn JavaScript in depth, I highly recommend Mosh’s JavaScript courses. The link to all of the courses are below: