DEV Community

loading...
Cover image for Modern JavaScript, 10 things you should be using, starting today
ITNEXT

Modern JavaScript, 10 things you should be using, starting today

softchris profile image Chris Noring Updated on ・7 min read

Follow me on Twitter, happy to take your suggestions on topics or improvements /Chris

You may be completely new to JavaScript or you may have only used it sporadically over the years. One thing is clear though - a lot has changed and there are some features YOU should be using. This article describes the features I think you should be using on a daily basis, if you are serious about JavaScript

Here is the video version:

Resources

These are my favorite resources for anything ES6+:

-1- Spread operator

This is denoted as a ... before an object or an array and accomplishes just what the name is saying, it's turning something from being a structure into a comma-separated list. Let's demonstrate this:

Spread array

let firstHalf = [ 'one', 'two'];
let secondHalf = ['three', 'four', ...firstHalf];

This is such a nice and compact way of writing it. Doing the same without this would mean doing something like this:

NO Array spread

let firstHalf = [ 'one', 'two'];


let secondHalf = ['three', 'four'];
for(var i=0, i <firstHalf.length; i++ ) {
  secondHalf.push(firstHalf[i]);
}


This can also be used on objects as a way of merging their properties:

Spread object

const hero = {
  name: 'Xena - Warrior Princess',
  realName: 'Lucy Lawless'
}


const heroWithSword = {
 ...hero,
 weapon: 'sword'
}

Doing this the hard way would be us looping through all the properties on the object:

NO Object spread

let keys = Object.keys(hero);
let obj = {};

for(var i=0; i< keys.length; i++) {
   obj[keys[i]] = keys[props[i]];
}

-2- Rest parameter

Rest parameters are about collecting the remaining parameters into an array. JavaScript has the ability to be flexible on the number of input parameters you give it. Normally there is an arguments variable that collects these. Let's look at what we mean:

function add(first, second, ...remaining) {
  return first + second;
}

Now, this above only summarize the parameters first and second. Which means invoking it with add(1,2) or add(1,2,3, 4) would yield the same results. To fix this we would type:

function add(first, second, ...remaining) {
  return first + second + remaining.reduce((acc, curr) => acc + curr, 0);
}

The above means we are fixing the problem and use all input parameters.

As stated earlier using the rest parameter, i.e adding a preceding ... as a way to collect the remaining parameters, is a way for us to name them and make it more explicit that we want to work with them. arguments have been around since at least ES5 but is less known I think.

-3- String interpolation

Have you ever seen a statement like this?

class Product {
 constructor(name, description, price) {
   this.name = name;
   this.description = description;
   this.price = price;
 }

 getDescription() {
   return " Full description \n" + 
   " name: " + this.name + 
   " description: " + this.description

 }
}

I'm of course talking about the getDescription() method, a long, multiline, hard-to-read statement. This is a reality in most programming languages. There is also string interpolation available in some languages and JavaScript is no different, fortunately. We can turn our getDescription() method into the following:

getDescription() {
   return `Full description \n: 
   name: ${this.name}
   description ${this.description}
   `;

 }

So double backticks ` is what we use to define a multi-line string. We also use ${} to interpolate. There your world is hopefully a lot better now :)

-4- Shorthand properties

You might be using this one without already knowing about it. In ES5 you had to write the following:

function createCoord(x, y) {
  return {
    x: x,
    y: y
  }
}

In ES6 and onwards you can omit the what's to the right of : if it has the same name, like so:

function createCoord(x, y) {
  return {
    x,
    y
  }
}

Looks less cluttered right?

-5- Method properties

This is how you define properties that point to methods in an object. Consider the following ES5 example:

const math = {
  add: function(a,b) { return a + b; },
  sub: function(a,b) { return a - b; }, 
  multiply: function(a,b) { return a * b; }
}

You don't actually need to have the whole add: business from ES6 and forwards. You can just type it like so:

const math = {
  add(a,b) { return a + b; },
  sub(a,b) { return a - b; },
  multiply(a,b) { return a * b; }
}

-6- Destructuring

Destructuring is about your own mental sanity as a developer.

Object destructuring

Consider the following code:

function handle(req, res) {
 const name = req.body.name;
 const description = req.body.description;
 const url = req.url;

 log('url endpoint', url);

 // lots of logic happening
 dbService.createPerson( name, description )
}

The code above is not perfect, in any way, but it does represent a case where we want to dig out data from an object at different levels. What's the problem you ask? Well, what if I didn't have to declare all those variables and save a few keystrokes? You can do just that:

function handle(req, res) {
 const { body: { name, description }, url }, = req;

 log('url endpoint', url);

 // lots of logic happening
 dbService.createPerson( name, description )

Above you see how three rows become one.

Array destructuring

This is not limited to objects. It can be done on arrays as well. Consider the following code:

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

const a = array[0];

const c = array[2];

This can be done in a much more elegant way, like so:

const array = [1,2,3,4,5,6];
const [a, ,c, ...remaining] = arr;

// remaining = [4,5,6]

We can just break out the values from the array by the above pattern matching. If we want to skip something we type , , and as a bonus, I threw in a REST statement to grab the remaining items.

Parameter matching

We can also do this on a function and its parameters. It has become the de-facto standard to collect all parameters in an object when you have more than 2-3 parameters in a function so you get a function looking like this:

function doSomething(config) {
  if(config.a) { ... }
  if(config.b) { ... }
  if(config.c) { ... }
}

The better way to do this is:

function doSomething({ a, b, c }) {
  if(a) { ... }
  if(b) { ... }
  if(c) { ... }
}

-7- Array methods

ES6 brought a whole slew of usable array methods like:

  • find(), finds an item in list else null
  • findIndex(), find the index of the item
  • some(), is the predicate true for at least one item in the list
  • includes(), is an item part of a list

Consider the following code to understand the usage:

const array = [{ id: 1, checked: true }, { id: 2 }];
arr.find(item => item.id === 2) // { id: 2 }
arr.findIndex(item => item.id === 2) // 1
arr.some(item => item.checked) // true

const numberArray = [1,2,3,4];
numberArray.includes(2) // true

-8- Promises + Async/Await

If you have been around the block a while you might remember a time when callbacks were all we had, like this:

function doSomething(cb) {
  setTimeout(() =>  {
    cb('done')
  }, 3000)
}

doSomething((arg) => {
 console.log('done here', arg);
})

We used this to handle the fact that some operations were asynchronous and simply took time to finish. Then we got promise libraries that people started using and eventually, we got native support in the language. So now we can do things like:

function doSomething() {
  return new Promise((resolve, reject) => {
    setTimeout(() =>  {
      resolve('done')
    }, 3000)
  })
}

doSomething().then(arg => {
 console.log('done here', arg);
})

We can even chain the whole experience so we can do calls like this:

getUser()
  .then(getOrderByUser)
  .then(getOrderItemsByOrder)
  .then(orderItems => {
    // do something with order items
  })

Async/await

Then we got async/await and life became even more glorious. Consider the above example with Promises now becoming this:

async function getItems() {
  try {
    const user = await getUser();
    const order = await getOrderByUser(user);
    const items = await getOrderItemsByOrder(order);
    return items;
  } catch(err) {
    // handle error here, the suggestion to return something or rethrow
  }
}

getItems().then(items => {
  // do something with order items
})

We get a synchronous-looking asynchronous code. :)

 -9- Modules

Pretty much any coding language supports the concept of modules. The ability to divide up your code in many different files, files that are also self-contained units, so-called modules. Consider the following code:

// math.js

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

export default (a,b) => a * b;

// main.js
import mult, { add, sub } from './math';

mult(2, 4) // 8
add(1,1)   // 2
sub(1,2)   // -1

Above we are using the export keyword to signal that these constructs add and sub is publically available for any module importing this module. The export default keyword is what we get if we just import it. In main.js we import the default as having name mult and we also specifically pick out methods add() and sub()

-10- Arrow functions + Lexical this

I've been using Arrow functions throughout this article and it's simply another function notation. In the past we could only write functions like this:

function printArray(arr) {
 // do something
}

Now we can define that as:

const printArray = (arr) => {
 // do something
}

One line functions

We can also define functions as one-liners:

const add = (a,b) => a + b

This automatically means we do the operation and return the result. We can do the same and return an object, our syntax then becomes:

const create = (a,b) = > ({ x: a, y: b })

Lexical this
The problem we used to face was no knowing what this is. Consider the following problem:

let array = [1,2,3];

function sum() {
  this.total = 0;

  arr.forEach(function(item) {
    this.total+= item;  // `this` is the inner functions `this`, BAD
  })
  return total;
} 

this in the above case points wrong inside of the forEach. The way we used to solve this was by doing:

function sum() {
  this.total = 0;
  var self = this;

  arr.forEach(function(item) {
    self.total+= item;  // now we are using `self`, it solves it but feels hacky
  })
  return total;
} 

Arrow functions fix this, no more self, so now the code looks like this:

function sum() {
  this.total = 0;

  arr.forEach((item) => {
    this.total+= item;  // all is well `this` points to outer function
  })
  return total;
} 

WINNING!

Summary

There are more things I could mention about ES6 and forward but I just wanted to show you my favorites that I think you should adopt today :)

Discussion (27)

pic
Editor guide
Collapse
seanmclem profile image
Seanmclem

My problem with async await is it seems to reduce options for error handling. Any way to mitigate that?

Collapse
softchris profile image
Chris Noring Author

hi. Yes you can have a try/catch inside of your async function, you can also have an error callback on the promise that invoking the async method will result in. I'll update the example, appreciate you raising this issue :)

Collapse
seanmclem profile image
Seanmclem

Thanks. I'd love to see how handling async/await errors can be done in a way that isn't just as complicated as then/catch

Thread Thread
natonathan profile image
Nathan Tamez

you can do

shop.getFreeKittens().then(kittens=>console.log(kittens), err=>{
  // do your thing
  console.error(err);
});
Thread Thread
softchris profile image
Chris Noring Author

yes, definitely that too. In my example that would be a catch-all handler. question I think was how to handle per call . So in promises it would be:

getUser()
  .then(getOrderByUser, errorHandler)
  .then(getOrderItemsByOrder, errorHandler)

The above scenario is what I think we are discussing.

For an error handling approach for async/await, we could do this:

async function get() {
  let user, order;
  try {
    user = await getUser();
  } catch(err) {}

 try {
  order = await getOrderByUser(user);
 } catch(err) {

 }
...
}

get()
  .then(successHandler, errorHandler)
Thread Thread
natonathan profile image
Nathan Tamez

I get the feeling that people can feel a bit intimidated when they first see a longer promise chain. this is were I believe async/await come in to their own, When used properly async functions can look pretty simple and straight forward. I still prefer promise style functions for most async tasks tho. Especially task like my example above.

Collapse
seanmclem profile image
Seanmclem

Can you return the items variable from outside the try{} block? Or would it be scoped inside the try block.

Thread Thread
natonathan profile image
Nathan Tamez • Edited

Yes you can, but if you return any value from a async function, the the promise that function returns will resolve, successfully.even if in the catch block of a try/catch block. This behavior can be vary useful. If you want the promise to throw an error you need to call

throw expression
Thread Thread
seanmclem profile image
Seanmclem • Edited

Okay, are you saying that a try catch block is not even necessary? Because the results of the error will get shoved into whatever is returned by the async function either way?

Collapse
metcoder95 profile image
Charlie Fuentes

When you work with async/await you have the option to use catch as well.

For example:

await someAsync().catch(console.error);
Collapse
ankurloriya profile image
Ankur Loriya

Use try catch

try {
     await UserService.create(name, email);
} catch(error) {
    // Handle error
}
Collapse
fref profile image
Fredrik Fall

Nice rundown of our new ES6 sugar :)
On the array remainder example I'm guessing there is a little typo:

const array = [1,2,3,4,5,6];
const [a, ,c, ...remaining];

I'm guessing should be

const array = [1,2,3,4,5,6];
const [a, ,c, ...remaining] = array;

Tack och hej :)

Collapse
guico33 profile image
guico33 • Edited
export default (a,b) => return a *b;

You'd need curly braces to be able to use return here, or just get rid of it.

const math = {
  add(a,b) => a + b
  sub(a,b) => a - b 
  multiply(a,b) => a *b
}

This syntax isn't valid. You cannot use arrow functions with the shorthand notation.

Collapse
softchris profile image
Chris Noring Author

Thanks, should be updated

Collapse
weakish profile image
Jang Rush

Hi, I'd like to translate this nice article to Chinese. Can you give me the permission? The translated text will be published at nextfe.com and there will be backlink to this original post at the beginning. Thanks.

Collapse
softchris profile image
Chris Noring Author

hi Jang sure. Please post here with a link to the chinese version once done, thanks :)

Collapse
weakish profile image
Jang Rush

Translated text has been published: nextfe.com/morden-js/

Collapse
pedroapfilho profile image
Pedro Filho • Edited

Hey, there's a typo on the first example of destructuring, I think you meant:

const name = req.body.name

Btw, nice article!!!

Collapse
softchris profile image
Chris Noring Author

Thanks Pedro :)

Collapse
veebuv profile image
Vaibhav Namburi

Clean and to the point! Good work mate!

Collapse
lxxxvi profile image
Mario

Very nice article, thank you. In 10. Arrow functions, the first example for printArray: can you really omit the parameter arr?

Collapse
softchris profile image
Chris Noring Author

hi Mario, no that's my mistake, I'll fix it, thanks :)

Collapse
skhmt profile image
Mike S

Two typos I found randomly:

Section 8: So know we can do things...

Section 9: this is publically available

Collapse
softchris profile image
Chris Noring Author

actually publically and publicly is too correct spellings.. appreciate you pointing it out Mike :)

Collapse
yuhijackman profile image
Yuhi Jackman

This is very useful and a great refresher on ES6. I really like this post!

Collapse
metcoder95 profile image
Charlie Fuentes

I think this features should be mandatory in any JavaScript project. With the exception of some projects in Node.js where there's no ES6 modules.

Collapse
softchris profile image
Chris Noring Author

agree.. they make the code smaller, more efficient, readable.. Hopefully, more people will use these features seeing this article. That was the goal :)

Collapse
metcoder95 profile image
Charlie Fuentes

Totally agree. Great article, I know will help a lot of people :)