Modern Javascript - I

Modern Javascript - I

Written by Piyush Sinha on Mar 6th, 2021 Views Report Post

ECMAScript 2015 (also known as ES6) is a major update to Javascript since ES5, which was standardized in 2009. Since then, Javascript has come up with incremental updates every year. These significant updates from ES6 and beyond are commonly referred to as Modern Javascript.

Template Literals

Template literals allow us to embed expressions in strings with a cleaner syntax.

Template literals are enclosed by the backtick character instead of double or single quotes.

// ES5
let name = "Piyush"
let msg = "Hello," + " " + name + "." // Hello, Piyush.
// ES6
let name = "Piyush"
let msg = `Hello, ${name}.` // Hello, Piyush.

Arrow Functions

Arrow functions are a syntactically compact alternative to a regular function expression. It makes your code more readable and structured.

// ES5
const isEven = function (num) {
 return num % 2 === 0;
}
// ES6
const isEven = num => num % 2 === 0;

Also, you can use Arrow functions with built-in functions like map, filter, and reduce.

const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const odds = nums.filter(n => n % 2 === 1);
console.log(nums); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(odds); // [1, 3, 5, 7, 9]

The handling of this is also different in arrow functions compared to regular functions. In regular functions, the this keyword represented the object that called the function, which could be the window, the document, or whatever. With arrow functions, the this keyword always represents the object that defined the arrow function.

Default Parameters

It allows named parameters to be initialized with default values if no value or undefined is passed.

// ES5
function multiply(a, b) {
  b = typeof b !== 'undefined' ? b : 1;
  return a * b;
}
console.log(multiply(5, 2)); // 10
console.log(multiply(5)); // 5
// ES6
function multiply(a, b = 1) {
  return a * b;
}
console.log(multiply(5, 2)); // 10
console.log(multiply(5)); // 5

Spread (…)

It allows an iterable such as an array expression or string to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected, or an object expression to be expanded in places where zero or more key-value pairs (for object literals) are expected.

Spread for function calls

Expands an iterable(array, string, etc.) into a list of arguments.

const nums = [1, 3, 2, 7, 5];
Math.max(nums); // NaN
// Use spread!
Math.max(...nums); // 7

Spread for array literals

Create a new array using an existing array. Spreads the elements from one array into a new array.

const nums1 = [1, 2, 3];
const nums2 = [4, 5, 6];
[...nums1, ...nums2]
// [1, 2, 3, 4, 5, 6]

Spread for object literals

Copies properties from one object into another object literal.

const name = {firstname: "piyush", lastname: "sinha"};                       
const name = {firstname: "piyush", lastname: "sinha"};
const fullAddress = {...address, country: "india"};
// {city: "mumbai", state: "maharashtra", country: "india"}
const details = {...name, ...fullAddress};
// {firstname: "piyush", lastname: "sinha", city: "mumbai", state: "maharashtra", country: "india"}

Destructuring

A short, clean syntax to unpack values from arrays or properties from objects into distinct variables.

Array Destructuring

const raceResults = ["Jazz", "Ibtesam", "Farhaz", "Kunal"];
const [gold, silver, bronze] = raceResults;
gold; // "Jazz"
silver; // "Ibtesam"
bronze; // "Farhaz"
const [fastest, ...everyoneElse] = raceResults;
fastest; // "Jazz"
everyoneElse; // ["Ibtesam", "Farhaz", "Kunal"]

Object Destructuring

const runner = {
  first: "Piyush",
  last: "Sinha", 
  country: "India"
}
const {first, last, country} = runner;
first; // "Piyush"
last; // "Sinha"
country; // "India"

Parameters Destructuring

const fullName = ({first, last}) => {
 return `${first} ${last}`
}
const runner = {
  first: "Piyush",
  last: "Sinha", 
  country: "India"
}
fullName(runner); // "Piyush Sinha"

Iteration over Arrays/Objects

for…of Loop

A nice and easy way of iterating over arrays.

const gamers = ["Piyush", "Jazz", "Ibtesam", "Farhaz", "Kunal"];
for(const player of gamers) {
  console.log(player);
}
// Piyush
// Jazz
// Ibtesam
// Farhaz
// Kunal

for…in Loop

A nice and easy way of iterating over all enumerable properties of an object.

const scores = {
  piyush: 80, 
  jazz: 86, 
  ibtesam: 92,
  farhaz: 90, 
  kunal: 88
}
for(const score in scores) {
  console.log(scores[score]);
}
// 80
// 86
// 92
// 90
// 88

Promises

A Promise is an object representing the eventual completion or failure of an asynchronous operation. It is a returned object to which you attach callbacks instead of passing callbacks into a function. They are easy to manage when dealing with multiple asynchronous operations where callbacks can create callback hell leading to unmanageable code.

The constructor syntax for a promise object is:

let promise = new Promise(function(resolve, reject) {  
 // do something
});

The constructor takes only one argument, a callback function. The callback function takes two arguments, resolve and reject. When the operation obtains the result, be it sooner or later, it doesn’t matter; it should call one of these callbacks:

  • resolve(value): if the operation finished successfully, with result value.
  • reject(error): if an error occurred, error is the error object.

The promise object returned by the new Promise constructor has these internal properties:

  • state — initially "pending", then changes to either "resolved" when resolve is called or "rejected"when reject is called.
  • result — initially undefined, then changes to value when resolve(value) called or error when reject(error) is called.

Let’s demonstrate a fake request using Promises to get more clarity:

const fakeRequestPromise = (url) => {
  return new Promise((resolve, reject) => {
    const delay = Math.floor(Math.random()*(4500)) + 500;
    setTimeout(() => {
      if(delay>4000) {
        reject("Connection Timeout!");
      } else {
        resolve(`Here is your fake data from ${url}`);
      }
    }, delay)
  })
}

I am using a random delay in setTimeout() to mimic an API request with a probability of success and failure.

If delay < 4000,

fakeRequestPromise("www.fakeapi.com/page1")
// Promise {[[PromiseStatus]]: pending, [[PromiseValue]]: undefined}
// Promise {[[PromiseStatus]]: resolved, [[PromiseValue]]: "Here is your fake data from www.fakeapi.com/page1"}

else,

fakeRequestPromise("www.fakeapi.com/page1")
// Promise {[[PromiseStatus]]: pending, [[PromiseValue]]: undefined}
// Promise {[[PromiseStatus]]: rejected, [[PromiseValue]]: "Connection Timeout!"}

The returned promise object can be consumed by registering functions using .then and .catch methods.

const request = fakeRequestPromise("www.fakeapi.com/page1");
request
   .then(() => {
      console.log("Promise Resolved");
      console.log("IT WORKED !!!");
   })
   .catch(() => {
      console.log("Promise Rejected");
      console.log("OH NO, ERROR !!!");
   })
//If delay < 4000
Promise Resolved
IT WORKED !!!
//else
Promise Rejected
OH NO, ERROR !!!

Promises are the ideal choice for handling asynchronous operations in the simplest manner. They can handle multiple asynchronous operations easily and provide better error handling than callbacks and events.

Conclusion

There is much, much more that Modern Javascript offers us to make our code syntactically cleaner and smarter. I hope I was able to introduce you to some of the ES6 features.

Comments (0)