JavaScript (ES7 | ES8 | ES9 | ES10)

This article will walk you through 'Javascript' upgrades post ES6. (Part-1)

If JavaScript is part of your dev stack or you have people around working with JavaScript, you would know how powerful and popular JavaScript has become since the release of ES6. ES6 was the biggest release of all time in JS history and a much-needed facelift JavaScript needed to come back to the game. After pushing these many upgrades in one shot, TC39 (Unit of ECMA international which takes care of standards of JavaScript) has decided to push a version out every single year, so it doesn’t become too heavy on developers to get aligned with it. So, all releases have come in subsequent years after that and not to mention they were very small releases compared to ES6.

Let’s see what are those upgrades.

ES7(2016)

  • Exponentiation operator

    The exponentiation operator (**) returns the result of raising the first operand to the power of the second operand. It is equivalent to Math.pow, except it also accepts BigInts as operands.

    
    2 ** 3 //  2 * 2 * 2 = 8
    2 ** 5 // 2*2*2*2*2 = 32
    
    
  • Array.prototype.includes

    The includes() method determines whether an array includes a certain value among its entries, returning true or false.

    
    [1,2,3,4].includes(2) // true
    ["q", "w", "e", "r", "t", "y"].includes("John") // false
    
    

ES8 (2017)

  • Async|await functions

    The async and await keywords enable asynchronous, promise-based behavior to be written in a cleaner style, avoiding the need to explicitly configure promise chains.

    Async function comes with one golden rule: It always returns a promise(resolved or rejected). The word “async” before a function makes a function async. Let's look at the syntax.

    
    async function foo(parameter1, parameter2, ...paramaterN) {
        // statements
    }
    
    // Check this out
    async function foo() {
      return 1;
    }
    foo().then(alert); // 1
    
    // We could explicitly return a promise, which would be the same:
    async function foo() {
      return Promise.resolve(1);
    }
    foo().then(alert); // 1
    
    

    The keyword await makes JavaScript wait until that promise settles and returns its result(Trick is, it works only inside of async functions). Let's look at the syntax:

    
    // works only inside async functions
    let value = await promise; 
    
    // Here’s an example with a promise that resolves in 1 second:
    async function foo() {
      let promise = new Promise((resolve, reject) => {
        setTimeout(() => resolve("done!"), 1000)
      });
      let result = await promise; // wait until the promise resolves (*)
      alert(result); // "done!"
    }
    
    foo();
    
    

    await literally suspends the function execution until the promise settles, and then resumes it with the promise result. That doesn’t cost any CPU resources, because the JavaScript engine can do other jobs in the meantime: execute other scripts, handle events, etc.
    It’s just a more elegant syntax of getting the promise result than promise.then. And, it’s easier to read and write.

    
    
    // Let's look at a real world scenerio where you would need data from first api call to pass to second and so on:
    
    function fetchData(url, userId, customerId){
      // return promise...
    }
    
    async function call(){
    const result1 = await fetchData(url) // data1 - JS will wait for this to complete to go to next line of execution.
    const result2 = await fetchData(url2, result1.id) // data2
    const result3 = await fetchData(url3, result1.id, result2.id) // data3
    }
    
    call();
    
    
    
  • Object.prototype.values

    The Object.values() method returns an array of a given object's own enumerable property values.

    
    const obj = {name: "Jhon", lastName: "Wick"}
    Object.values(obj) // ["John", "Wick"]
     
    
  • Object.prototype.entries

    The Object.entries() method returns an array of a given object's own enumerable string-keyed property [key, value] pairs.

    
    const obj = {name: "Jhon", lastName: "Wick"}
    Object.entries(obj) // [["name", John"], ["lastName", Wick"]]
    
    
  • Object.prototype.getOwnPropertyDescriptors

    The Object.getOwnPropertyDescriptors() method returns all own property descriptors of a given object.

    
    const obj = {name: "Jhon", lastName: "Wick"}
    const descriptors = Object.getOwnPropertyDescriptors(obj); 
    
    console.log(descriptors) // { name: { value: "John", writable: true, enumerable: true, configurable: true } }
    console.log(descriptors.name.writable) // true
    
    
  • String.prototype.padStart and String.prototype.padEnd

    The padStart() and padEnd() method pads the current string with another string (multiple times, if needed) until the resulting string reaches the given length. The padding is applied from the start of the current string with padStart and from the end with padEnd.

    
    “abc”.padStart(10, 1) // “1111111abc”
    “abc”.padStart(10) // “       abc”
    “abc.padStart(6, “qwerty”) // “qweabc”
    
    “abc”.padEnd(10, 1) // “abc1111111”
    “abc”.padEnd(10) // “abc       ”
    “abc.padEnd(6, “qwerty”) // “abcqwe”
    
    
    

ES9(2018)

  • Asynchronous iteration

    a variation of the for-of iteration statement which iterates over async iterable objects. Let's look at the example to understand this:

    
    async function fetchJSON(){
      const promises = [
      fetchJSON(file1.json),
      fetchJSON(file2.json),
      fetchJSON(file3.json),
      fetchJSON(file4.json),
      ]
    
      // Normal Iterator
      for(const x of promises){
        console.log(x) // logs promise...
      }
    
    
      // Async Iterator
      for await(const x of promises){
        console.log(x) // logs a resolved response.
      }
    }
    
    
  • Rest|Spread into object literals

    Rest and Spread go hand in hand. Rest allows us to pass an indefinite number of parameters to a function and access them in an array where The spread operator “spreads” the values in an iterable (arrays, strings) across zero or more arguments or elements.

    The Rest/Spread Properties for ECMAScript proposal (ES2018) added Rest properties for object destructuring assignment and Spread properties for object literals. It copies own enumerable properties from a provided object onto a new object.

    Let's look at an example to understand how spreading works with an object.

    
    // Rest properties
    const {name, ...others} = {name: "John", lastName: "Wick", profession: "BogeyMan"}
    console.log(name) // "John"
    console.log(others) // {lastName: "Wick", profession: "BogeyMan"}
    
    
    // Spread properties
    const hero = {name, ...others}
    console.log(hero) // name: "John", lastName: "Wick", profession: "BogeyMan"}
    
    
    // This will give TypeError
    let obj = {'key1': 'value1'};
    let array = [...obj]; // TypeError: obj is not iterable
    
    
  • Promise.prototype.finally

    A finally callback execute logic once your Promise has been settled one way or the other. It has absolutely no impact on the value that your promise will resolve to.

    The idea of finally is to set up a handler for performing cleanup/finalizing after the previous operations are complete. E.g. stopping loading indicators, closing no longer needed connections etc.

    A finally handler has no arguments. In finally we don’t know whether the promise is successful or not. That’s all right, as our task is usually to perform “general” finalizing procedures and the promise outcome is handled in the next handler.

    
    new Promise((resolve, reject) => {
      setTimeout(() => resolve("value"), 2000);
    })
      .finally(() => alert("Promise ready")) // triggers first
      .then(result => alert(result)); // <-- .then shows "value"
    
    

ES10(2019)

  • Optional catch binding

    The Optional Catch Binding allows us to use the try/catch statement without passing the error parameter inside the catch block. It is optional for you whether you want to pass the error variable or not.

    
    // Wihout optional catch binding
      try {
        throw new Error("Some random error");
      } catch (error) {
        console.log(error)
      }
    
    // With optional catch binding
      try {
        throw new Error("Hey");
      } catch {
        console.log("No parameter for catch");
      }
    
    
  • Object.prototype.fromEntries

    This method is used to transforms a list of key-value pairs into an object. This method returns a new object whose properties are given by the entries of the iterable

    
    const mapArr = new Map(
        [['student1', 'Intern'],
        ['student2', 'Manager']]
    );
    const person = Object.fromEntries(mapArr);
    console.log(person); // Object { student1: "Intern", student2: "Manager" }
    
    
  • String.prototype.{trimStart,trimEnd}

    The trimStart(alias~trimLeft) and trimEnd(alias~trimRight) method removes whitespace from the string. The trimStart() method removes whitespace from the beginning of a string where trimEnd() method removes whitespace from the end of a string.

    
    const greeting = '   Hello world!   ';
    console.log(greeting); // "   Hello world!   ";
    
    console.log(greeting.trimStart()); // "Hello world!   ";
    console.log(greeting.trimEnd()); // "Hello world!";
    
    
  • Array.prototype.{flat,flatMap}

    The flat() method creates a new array with all sub-array elements concatenated into it recursively up to the specified depth.
    Let's look at few examples:

    
    const arr = [1,2,3,[4,5]]
    console.log(arr.flat()) // [1, 2, 3, 4, 5]
    
    const arr2 = [1,2,3,[4,5,[6,7,[8,9]]]]
    console.log(arr2.flat()) // [1, 2, 3, 4, 5, [6, 7, [8, 9]]]
    console.log(arr2.flat(2)) // [1, 2, 3, 4, 5, 6, 7, [8, 9]]
    console.log(arr2.flat(3)) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
    
    

    The flatMap() method returns a new array formed by applying a given callback function to each element of the array, and then flattening the result by one level. It is identical to a map() followed by a flat() of depth 1.
    Let's look at few examples:

    
    const arr1 = [1, 2, 3, 4];
    
    arr1.map(x => [x * 2]); // [[2], [4], [6], [8]]
    arr1.flatMap(x => [x * 2]); // [2, 4, 6, 8]
    
    // only one level is flattened
    arr1.flatMap(x => [[x * 2]]); // [[2], [4], [6], [8]]
    
    
  • Well-formed JSON.stringify

    JSON.stringify was previously specified to return ill-formed Unicode strings if the input contains any lone surrogates:

    
    JSON.stringify('\uD800'); // → '"�"'
    
    

    The well-formed JSON.stringify proposal changes JSON.stringify so it outputs escape sequences for lone surrogates, making its output valid Unicode (and representable in UTF-8):

    
    JSON.stringify('\uD800'); // → '"\\ud800"'
    
    
Series: JS Upgrades

JavaScript (ES11 | ES12 | ES13)

This article will walk you through 'Javascript' upgrades post ES10. (Part-2)

Read More