Categories
JavaScript

Useful New Features in ES2016 and 2017

Spread the love

ES2015 introduced lots of features, like arrow functions, class syntax for object creation and inheritance, let and const, and many other new features. In the next few years, more great features will be introduced into the language and the standard library. ES2016 was a minor revision with new features, such as introducing the includes function to arrays and the exponential operator. ES2017 introduced more features like Object.values and Object.entries and string functions like padStart and padEnd, and async and await. These are handy features that bring even more convenience to JavaScript developers, making JavaScript app development even easier.


ES2016 Features

Array.includes

Array.includes checks if an item exists in an array. It takes a number or string, which the function can compare as the first argument and the index to search from as the second argument. The second argument can be positive or negative.

const array = [1,2,3];  
const includesTwo = array.includes(2); // returns true

We can also search the array starting with a negative index, then the function search beginning from the computed index, which is the length of the array plus the second argument. So if we write:

array.includes(2, -2);

Then the includes function will search starting with index 3 +(-2). This means that array.includes(2, -2); should return true. If the absolute number of what you pass in is bigger than the length, then the whole array is searched.

The includes function was also added to TypedArray types like Int8Array , or Uint8Array .

Exponential operator

The exponential operator is another new feature of ES2016. The exponential operator is denoted by **. It’s the new syntax for computing exponential values, which is the alternative to the Math.pow function. For example, to compute 2 to the power of 3, we can write,

2**3

which is 8. Note that in JavaScript, it’s impossible to write ambiguous expressions when we combine the exponential operator with unary operators like -, so something like -2**2 is invalid in JavaScript. However, -(2**2) is valid since it’s unambiguous in that we know that the value is -4 since the exponential is in parentheses and the negative sign is outside.

We can force the base of the exponentiation expression to be a negative number by writing:

(-2)**2

So this means the base has to be in parentheses.


ES2017 Features

Object.values

The Object.values lets us get an array with the values of an object in an array. The values are returned in the same order as it is provided by the for...in loop, except that Object.values do not return values that are in the prototype chain.

For example, we can write:

const obj = { a: 1, b: 2 };  
console.log(Object.values(obj)); // [1, 2]  
  
const obj = { 0: 'd', 1: 'e', 2: 'f' };  
console.log(Object.values(obj)); // ['d', 'e', 'f']  
  
const obj2 = { 100: 'd', 2: 'e', 7: 'f' };  
console.log(Object.values(obj2)); // ['e', 'f', 'd']  
  
console.log(Object.values('abc')); // ['a', 'b', 'c']

As we can see, Object.values works with objects and strings. If the keys are numbers, then they are returned in with the keys in increasing order, like in the obj2 example above. For strings, it returns an array of the individual characters of the string.

Object.entries()

The Object.entries function returns an array with each entry being the key-value pairs in their individual arrays. The entries are returned in the same order as it is provided by the for...in loop, except that Object.values do not return values that are in the prototype chain. As with any object, we can sort the array to get the entries in the order we want with the sort function.

For example, we can write:

const obj = { foo: 1, bar: 2 };  
console.log(Object.entries(obj)); // [ ['foo', 1], ['bar', 2] ]  
  
const obj = { 0: 'x', 1: 'y', 2: 'z' };  
console.log(Object.entries(obj)); // [ ['0', 'x'], ['1', 'y'], ['2', 'z'] ]  
  
const obj2 = { 100: 'x', 2: 'y', 7: 'z' };  
console.log(Object.entries(obj2)); // [ ['2', 'x'], ['7', 'y'], ['100', 'z'] ]  
  
console.log(Object.entries('abc')); // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]  
  
console.log(Object.entries(100)); // [ ]  
  
const obj = { a: 1, b: 2, c: 3};  
for (const [key, value] of Object.entries(obj)) {  
  console.log(`${key} ${value}`); // "a 1", "b 2", "c 3"  
}  
  
Object.entries(obj).forEach(([key, value]) => {  
  console.log(`${key} ${value}`); // "a 1", "b 2", "c 3"  
});

In the above examples, we can see that Object.entries return an array of key-value pairs with each entry as an array with the key being the first element and the value being the second element. If the keys are integers, then they are sorted in ascending numerical order. So Object.entries(obj2) returns [ ['2', 'x'], ['7', 'y'], ['100', 'z'] ]. It also works with strings with the individual characters return with the index of the characters as the key, so the key is the index of the string, which is the first element of each entry, and the individual character is the value of the string, which is returned as the second character. For objects that have no properties, like numbers and booleans, Object.entries returns an empty array.

The array returned by Object.entries can be converted to a Map object. It can be used like in the following example:

const obj = { a: 1, b: 2 };   
const map = new Map(Object.entries(obj));  
console.log(map); // Map { a: 1, b: 2 }

String.padStart()

The padStart() function adds a string the number of times before a string until it reaches the length that you specify. The function takes two parameters. The first is the target length of your string. If the target length is less than the length of your string, then the string is returned as-is. The second parameter is the string that you want to add the padding with. It’s an optional parameter and it defaults to ' ' is nothing is specified.

For example, we can write the following:

'def'.padStart(10);         // "       def"  
'def'.padStart(10, "123");  // "1231231def"  
'def'.padStart(6,"123465"); // "abcdef"  
'def'.padStart(8, "0");     // "00000def"  
'def'.padStart(1);          // "def"

Note that each string is filled up to the target length with the string in the second argument. The whole string in the second argument may not always be included. Only the part that lets the function fill the string up to the target length is included.

String.padEnd()

The padEnd() function adds a string the number of times after a string until it reaches the length that you specify. The function takes 2 parameters. The first is the target length of your string. If the target length is less than the length of your string, then the string is returned as is. The second parameter is the string that you want to add the padding with. It’s an optional parameter, and it defaults to ' ' is nothing is specified.

For example, we can write the following:

'def'.padEnd(10);         // "def       "  
'def'.padEnd(10, "123");  // "def1231231"  
'def'.padEnd(6,"123465"); // "defabc"  
'def'.padEnd(8, "0");     // "def00000"  
'def'.padEnd(1);          // "def"

Object.getOwnPropertyDescriptors()

The Object.getOwnPropertyDescriptors() function returns all the property descriptors of an object.

For example, we can use it like the following code:

const obj = {  
  a: 1  
};const descriptors = Object.getOwnPropertyDescriptors(obj);

The descriptors object should have:

{  
  a: {  
    configurable: true,  
    enumerable: true,  
    value: 1,  
    writable: true  
  }  
}

Async and Await

With async and await, we can shorten promise code. Before async and await, we have to use the then function, we make to put callback functions as an argument of all of our then functions. This makes the code long is we have lots of promises. Instead, we can use the async and await syntax to replace the then and its associated callbacks as follows. For example, we have the code to make requests to the back end with the Fetch API:

const APIURL = "http://localhost:3000";
const subscribe = async data => {  
  const response = await fetch(`${APIURL}/subscribers`, {  
    method: "POST",  
    mode: "cors",  
    cache: "no-cache",  
    headers: {  
      "Content-Type": "application/json"  
    },  
    body: JSON.stringify(data)  
  });  
  return response.json();  
};

window.onload = () => {  
  nameForm.method = "post";  
  nameForm.target = "_blank";  
  nameForm.action = "";  
  nameForm.addEventListener("submit", async e => {  
    e.preventDefault();  
    const firstName = document.getElementById("firstName").value;  
    const lastName = document.getElementById("lastName").value;  
    const email = document.getElementById("email").value;  
    let errors = [];  
    if (!firstName) {  
      errors.push("First name is required.");  
    } 

    if (!lastName) {  
      errors.push("Last name is required.");  
    } 

    if (!email) {  
      errors.push("Email is required.");  
    } 

    if (!/\[^@\]+@\[^\.\]+\..+/.test(email)) {  
      errors.push("Email is invalid.");  
    } 

    if (errors.length > 0) {  
      alert(errors.join(" "));  
      return;  
    }  
    try {  
      const response = await subscribe({  
        firstName,  
        lastName,  
        email  
      });  
      alert(`${response.firstName} ${response.lastName} has subscribed`);  
    } 
    catch (error) {  
      alert(error.toString());  
    }  
  });  
};

We replaced the then and callbacks with await. Then we can assign the resolved values of each promise as variables. Note that if we use await for our promise code then we have to put async in the function signature as we did in the above example. To catch errors, instead of chaining the catch function in the end, we use the catch clause to do it instead. Also, instead of chaining the finally function at the bottom to run code when a promise ends, we use the finally clause after the catch clause to do that instead.

In the code above, we got the resolved value of the promise assigned to a variable instead of getting the value in the callback of the then function, like in the const response = await subscribe({...}) line above.

async functions always return promises and cannot return anything else like any other function that uses promises. In the example above, we used the promised based Fetch API and async and await syntax, we showed that we can chain promises in a much shorter way than with the then function with callbacks passed in as an argument.

ES2016 and 2017 gave us convenient features that enhance the already great syntactic sugar and functions that were added to ES2015, which was a significant improvement over its predecessor. These two versions of JavaScript make JavaScript development even better with new Object, string, and Array methods and shorthand for chaining promises.

Leave a Reply

Your email address will not be published.

If you like the content of this blog, subscribe to my email list to get exclusive articles not available to anyone else.