DEV Community

Cover image for New ES2019 JavaScript features every developer should be excited about
Brian Neville-O'Neill
Brian Neville-O'Neill

Posted on • Originally published at blog.logrocket.com on

New ES2019 JavaScript features every developer should be excited about

Written by Gbolahan Olagunju✏️

JavaScript has come a long way since its early days, with many new additions and features designed specifically to make the language more user-friendly and less verbose. Below are some recent additions to JavaScript that I find fascinating.

Some of these features are already available in Node, Chrome, Firefox, and Safari, while others are still in the proposal stage.

LogRocket Free Trial Banner

Optional chaining

Optional chaining is done using the ?. operator. It primarily ensures that the preceding value before the question mark is neither undefined nor null. This is really useful when assessing the properties of deeply nested objects.

There’s a need to be sure that the ?. operator exists before assessing the properties.

Consider the following example:

const users = [
  {
   name: "Olagunju Gbolahan",
   occupation: "Software Developer",
   sayName(){
    console.log(`my name is ${this.name}`);
   },
   address: { office: "New York" }
  },
  { name: "Olawaseyi Moses" },
  { name: "Tunde Ednut" }
];
Enter fullscreen mode Exit fullscreen mode

Let’s consider the second user in the array of users:

const secondUser = users[1];
Enter fullscreen mode Exit fullscreen mode

We may want to get the office address of this user. Before the advent of the optional chaining operator, we would have to go through a relatively inefficient process to obtain this information:

const theAddress = secondUser.address && secondUser.address.office;
console.log(theAddress); // undefined
Enter fullscreen mode Exit fullscreen mode

If we had a deeply nested object, we would have to check that its value existed using && operator on each level.

But with the optional chaining, we simply do the following:

const theAddress = secondUser?.address?.office;
console.log(theAddress); // undefined
Enter fullscreen mode Exit fullscreen mode

We can also use optional chaining with object methods to confirm they exist before execution:

const firstUser = users[0];
console.log(firstUser.sayName?.()); // my name is Olagunju Gbolahan
Enter fullscreen mode Exit fullscreen mode

It will simply return undefined if a method with the given name doesn’t exist on the object.

console.log(firstUser.sayOccupation?.()); // undefined
Enter fullscreen mode Exit fullscreen mode

Because the optional chaining operator hasn’t been added to the JavaScript specification yet, it is still in the proposal stage.

You can use it today with the babel-plugin-proposal-optional-chaining plugin.

Optional catch binding

This feature comes in handy when we know beforehand what our error will be, and we don’t want the redundancy of unused variables.

Consider the traditional try and catch block:

try {
  const parsedJsonData = JSON.parse(obj);
} catch (error) {
  //the variable error has to be declared weather used or unused
  console.log(obj);
}
Enter fullscreen mode Exit fullscreen mode

But with the addition of optional catch binding, we don’t have to provide unused variables — especially when we have defaults for our try block.

function getName () {
  let name = "Gbolahan Olagunju";
  try {
    name = obj.details.name
  } catch {}
  console.log(name);
}
Enter fullscreen mode Exit fullscreen mode

The pipeline operator

This is one of the proposed additions to Javascript and it is currently at stage 1.

It essentially helps make several function calls to the same argument readable.

It does this by piping the value of an expression as argument(s) to a function. Consider calling the following functions without the pipeline operator |>.

const capitalize = (input) =>  input[0].toUpperCase() + input.substring(1);
const removeSpaces = (input) => input.trim();
const repeat = (input) => `${input}, ${input}`;
Enter fullscreen mode Exit fullscreen mode
const withoutpipe = repeat(capitalize(removeSpaces('    i am gbols    ')));
console.log(withoutpipe); // I am gbols, I am gbols
Enter fullscreen mode Exit fullscreen mode

But with the pipeline operator, readability can be greatly improved:

const withpipe = '    i am gbols    '
                |> removeSpaces
                |> capitalize
                |> repeat;
console.log(withpipe); // // I am gbols, I am gbols
Enter fullscreen mode Exit fullscreen mode

String.trimStart and String.trimEnd

This was formally named trimRight and trimLeft, but with ES2019 the names were changed to the aliases trimStart and trimEnd to make them more intuitive to users.

Consider the following example:

let massage = "     Welcome to LogRocket      ";
message.trimStart(); // "Welcome to LogRocket      "
message.trimEnd(); // "Welcome to LogRocket";
Enter fullscreen mode Exit fullscreen mode

Object.fromEntries

Before talking about Object.fromEntries, it is important to talk about Object.entries.

The Object.entries method was added to the ES2017 specification to provide a way to convert an object into its array equivalent, thus granting it access to all array methods for processing.

Consider the following object:

const devs = {
  gbols: 5,
  andrew: 3,
  kelani: 10,
  dafe: 8,
};
const arrOfDevs = Object.entries(devs);
console.log(arrOfDevs);
//[
//  ["gbols", 5]
//  ["andrew", 3]
//  ["kelani", 10]
//  ["dafe", 8]
//]
Enter fullscreen mode Exit fullscreen mode

Now, we can use the filter method on arrays to get devs that have more than 5 years experience:

const expDevs = arrOfDevs.filter(([name, yrsOfExp]) => yrsOfExp > 5);
console.log(expDevs);
//[
//  ["kelani", 10]
//  ["dafe", 8]
//]
Enter fullscreen mode Exit fullscreen mode

Then a problem arises: there is no easy way to convert the results back into an object. Usually, we would write our own code to transform this back into an object:

const expDevsObj = {};
for (let [name, yrsOfExp] of expDevs) {
expDevsObj[name] = yrsOfExp;
}
console.log(expDevsObj);
//{
 //dafe: 8
 //kelani: 10
//}
Enter fullscreen mode Exit fullscreen mode

But with the introduction of Object.fromEntries, we can do this in one swipe:

console.log(Object.fromEntries(expDevs));
//{
 //dafe: 8
 //kelani: 10
//}
Enter fullscreen mode Exit fullscreen mode

Flat and FlatMap

Oftentimes, we have deeply nested arrays to deal with as a result of an API call. In this case, it’s especially important to flatten the array.

Consider the following example:

const developers = [
  {
    name: 'Gbolahan Olagunju',
    yrsOfExp: 6,
    stacks: ['Javascript', 'NodeJs', ['ReactJs', ['ExpressJs', 'PostgresSql']]]
  },
  {
    name: 'Daniel Show',
    yrsOfExp: 2,
    stacks: ['Ruby', 'Jest', ['Rails', ['JQuery', 'MySql']]]
  },
  {
    name: 'Edafe Emunotor',
    yrsOfExp: 9,
    stacks: ['PHP', 'Lumen', ['Angular', 'NgRx']]
  }
];
Enter fullscreen mode Exit fullscreen mode
const allStacks = developers.map(({stacks}) => stacks);
console.log(allStacks);
// [
// ['Javascript', 'NodeJs', ['ReactJs', ['ExpressJs', 'PostgresSql']]]
// ['Ruby', 'Jest', ['Rails', ['JQuery', 'MySql']]]
// ['PHP', 'Lumen', ['Angular', 'NgRx']]
// ]
Enter fullscreen mode Exit fullscreen mode

The allstacks variable contains deeply nested arrays. To flatten this array, we can use the Array.prototype.flat.

Here’s how:

const flatSingle = allStacks.flat();
console.log(flatSingle);
//[
// "JavaScript",
//  "NodeJs",
// ['ReactJs', ['ExpressJs', 'PostgresSql']]]
// "Ruby",
// "Jest",
// ['Rails', ['JQuery', 'MySql']]]
// "PHP",
// "Lumen"
// ["Angular", "NgRx"]
//]
Enter fullscreen mode Exit fullscreen mode

We can deduce from the above that the array has been flattened one level deep, which is the default argument to array.prototype.flat.

We can pass an argument to the flat method to determine the degree to which we want to flatten.

The defaults argument is a value of 1. To completely flatten the array, we can pass an argument of Infinity. The Infinity argument flattens the array completely, irrespective of the depth of the array.

Here’s how:

const completelyFlat = allStacks.flat(Infinity);
console.log(completelyFlat);
//[
// "JavaScript",
// "NodeJs",
// "ReactJs",
// "ExpressJs",
// "PostgresSql",
// "Ruby",
// "Jest",
// "Rails",
// "JQuery",
// "MySql",
// "PHP",
// "Lumen",
// "Angular",
// "NgRx"
//]
Enter fullscreen mode Exit fullscreen mode

FlatMap

FlatMap is a combination of calling the map method and the flat method with a depth of 1. It is often quite useful as it does the same thing in a very efficient manner.

Below is a simple example of using both map and flatMap:

let arr = ['my name is Gbols', ' ', 'and i am great developer']; 
console.log(arr.map(word => word.split(' ')));
//[
// ["my", "name", "is", "Gbols"],
// ["", ""],
// ["and", "i", "am", "great", "developer"]
//]
Enter fullscreen mode Exit fullscreen mode
console.log(arr.flatMap(word => word.split(' ')));
//[ "my"
//  "name"
//  "is"
//  "Gbols"
//   ""
//   ""
//   "and"
//   "i"
//   "am"
//   "great"
//   "developer"
//]
Enter fullscreen mode Exit fullscreen mode

Conclusion

In this article, we’ve counted the many benefits of new additions to JavaScript. These additions enhance the developer experience by reducing verbosity and increasing readability.

Below, check out a couple new features that we didn’t cover:

JSON.stringify

Sort stability


Editor's note: Seeing something wrong with this post? You can find the correct version here.

Plug: LogRocket, a DVR for web apps

 
LogRocket Dashboard Free Trial Banner
 
LogRocket is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
 
Try it for free.


The post New ES2019 JavaScript features every developer should be excited about appeared first on LogRocket Blog.

Top comments (14)

Collapse
 
thealiilman profile image
Ali Ilman

Optional chaining is something I'm so delighted to see.
All of us have written obj.property && obj.property.anotherProperty, and it's a pain to the eyes when the properties are long.

Collapse
 
victorcorcos profile image
Victor Cordeiro Costa

I hate to be that guy, but I need to nitpick some details of this article. My intention is just to help!

This section...

  • String.trimStart and String.trimEn

should be this

  • String.trimStart and String.trimEnd

Am I right?

And regarding the Optional catch binding, is the structure of both codes really similar?

Take a look at the below code, the console.log() line will only be executed if the try block fail

try {
  const parsedJsonData = JSON.parse(obj);
} catch (error) {
  //the variable error has to be declared weather used or unused
  console.log(obj);
}

Now, take a look at the below code, which have the optional catch binding ... the console.log() will be executed whether the try block fails or not. Not only when it fail, like the previous version.

function getName () {
  let name = "Gbolahan Olagunju";
  try {
    name = obj.details.name
  } catch {}
  console.log(name);
}

I am thinking here... maybe the similar structure will be something like this...

function getName () {
  let name = "Gbolahan Olagunju";
  try {
    name = obj.details.name
  } catch {
    console.log(name);
  }
}

Am I right?

Thanks!

Great article, I personally love the optional chaining operator ?., this makes the code so much more readable. However, it is good to mention that we need to avoid calling it when it it is not necessary, otherwise we can have unnecessary performance issues and unclear code behaviors. 👍

Collapse
 
tclain profile image
Timothée Clain

pipeline operator !! yay

Collapse
 
anshap1719 profile image
Anshul Sanghi

Wow, I am working on a project right now that could literally use all of these features on an extensive level. I’ve found ts-optichain for optional chaining very good for now but the rest, I would really love to see. Is there a way to use these features with babel maybe?

Collapse
 
samanocedillo profile image
Samano Cedillo

Buen post!

Gracias por el aporte.

Collapse
 
danielo515 profile image
Daniel Rodríguez Rivero

Is this real life? or is just fanta-sea?
By the way, I don't see why would we need to add an empty catch block, it will be better to make it entirely optional.

Collapse
 
mateiadrielrafael profile image
Matei Adriel

U should open a proposal or something, amazing idea

Collapse
 
gwigwam profile image
GWigWam

Hey, small detail:

In the code for trimStart/trimEnd you misspell 'massage'. Also, the output of message.trimEnd(); would still have the spaces at the start (left) of the string right?

Collapse
 
giancorzo profile image
Giancarlo Corzo

Nice article. Just a little bug. A quote is missing in the function sayName() console.log

Collapse
 
zooly profile image
Hugo Torzuoli

Indeed missing backtick at the start of the log

Collapse
 
manuelojeda profile image
Manuel Ojeda

Nested chaining 👌

Collapse
 
evolutionxbox profile image
Jonathan Cousins

Hasn’t ECMAScript 2019 already been released?

Collapse
 
mogery profile image
Gergő Móricz

WOOT FOR OPTIONAL CHAINING!

Collapse
 
nestordgs profile image
Nestor Gutiérrez

Can't wait to see Optional Chaining