2 ES6 - theoriginalvisagie/JavaScript-Algorithms-and-Data-Structures GitHub Wiki

What is ES6?

ECMAScript, or ES, is a standardized version of JavaScript. Because all major browsers follow this specification, the terms ECMAScript and JavaScript are interchangeable.

ES6, released in 2015, added many powerful new features to the language.

Let and Var:

When you declare a variable with the var keyword, it is declared globally, or locally if declared inside a function.

The let keyword behaves similarly, but with some extra features. When you declare a variable with the let keyword inside a block, statement, or expression, its scope is limited to that block, statement, or expression.

ex.

var numArray = [];
for (var i = 0; i < 3; i++) {
  numArray.push(i);
}
console.log(numArray);
console.log(i);

Here the console will display the values [0, 1, 2] and 3.

With the var keyword, i is declared globally. So when i++ is executed, it updates the global variable.

This behavior will cause problems if you were to create a function and store it for later use inside a for loop that uses the i variable. This is because the stored function will always refer to the value of the updated global i variable.

The let keyword does not follow this behavior:

let printNumTwo;
for (let i = 0; i < 3; i++) {
  if (i === 2) {
    printNumTwo = function() {
      return i;
    };
  }
}
console.log(printNumTwo());
console.log(i);

Here the console will display the value 2, and an error that i is not defined.

i is not defined because it was not declared in the global scope.

Mutating arrays:

Some developers prefer to assign all their variables using const by default, unless they know they will need to reassign the value. Only in that case, they use let.

NB Object assigned with const are still mutable, including arrays and functions.

const s = [5, 6, 7];
s = [1, 2, 3];
s[2] = 45;
console.log(s);
// s = ... will produce an error. 
// Comment out that line adn the output will be [5, 6, 45].

The above error will be displayed, because const was used. You cannot use the variable identifier s to point to a different array using the assignment operator.

Preventing mutation.

As seen above even const cannot prevent mutation. So how do we prevent mutation from happening?

JavaScript has a function called Object.freeze that prevents it from being mutated.

let obj = {
  name:"FreeCodeCamp",
  review:"Awesome"
};
Object.freeze(obj);
obj.review = "bad";
obj.newProp = "Test";
console.log(obj); 

You will receive an error if you try and run the script above, because it runs in strict mode.

Inline Functions.

If we write a function and we do not plan on reusing it, then we can write inline functions. We do not need to name these functions, because of their one use nature.

In ES6 we are provided with syntactic sugar to write function with arrow syntax

const myFunc = () => {
    const myVar = "value";
    return myVar;
}

When we only have a return value we can shorten it even more.

const myFunc = () => "value";

Adding parameters:

We can pass parameters to inline functions just as we can to normal ones.

const doubler = (item) => item * 2;
doubler(4);
// Output will be 8.

If we have a single parameter we can get rid of the brackets.

const doubles = item => item * 2;
doubler(4);
// Output will be 8.

We can pass more than one parameter, we just add the brackets back in and separate the parameter with a comma.

const doubles = (item, multi) => item * multi;
doubles(4,4);
// Output is 16.

We can even assign default paramaters to out inline functions as follows:

const doubles = (item, multi = 2) => item * multi;
doubles(4);
// Output is 8.
doubles(5,5);
// Output is 25.

Rest Parameter:

In order to help us create more flexible functions, ES6 introduces the rest parameter for function parameters. With the rest parameter, you can create functions that take a variable number of arguments. These arguments are stored in an array that can be accessed later from inside the function.

function howMany(...args) {
  return "You have passed " + args.length + " arguments.";
}
console.log(howMany(0, 1, 2));
// Output is You have passed 3 arguments.
console.log(howMany("string", null, [1, 2, 3], { }));
// Output is You have passed 4 arguments.

Spread operator:

The spread operator allows us to expand arrays and other expressions in places where multiple parameters or elements are expected.

const arr = [6, 89, 3, 45];
const maximus = Math.max(...arr);

...arr returns an unpacked array or spread array.

Destructuring assignment:

This is special syntax introduced in ES6, for neatly assigning values taken directly from an object.

In ES5 we would've done the following:

const user = {
    name: "John Doe",
    age: 34
}

const name = user.name;
const age = user.age;

This can all be simplified with ES6:

const user = {
    name: "John Doe",
    age: 34
}

const {name, age} = user;

Assigning Variables:

Destructuring allows you to assign a new variable name when extracting values. You can do this by putting the new name after a colon when assigning the value.

const user = { 
    name: 'John Doe', 
    age: 34 
};

const { name: userName, age: userAge } = user;

Here we assign userName the value of user.name and the same for userAge.

Assigning Variables from Nested Objects:

We can use the same method as above to assign variables from nested objects:

const user = {
    johnDoe{
        age:34,
        email: "[email protected]"
    }
};

const {johnDoe: {age, email}} = user;
// Here age and email will have the same values as the objects age and email.

const {johnDoe: {age:userAge, email:userEmail}} = user;
// Here the new variables are named "userAge" and "userEmail" and the will be assigned as in the above line.

Assigning Variables from Arrays:

One key difference between the spread operator and array destructuring is that the spread operator unpacks all contents of an array into a comma-separated list. Consequently, you cannot pick or choose which elements you want to assign to variables.

const [a, b] = [1, 2, 3, 4, 5, 6];
console.log(a, b);
// Output will be 1,2.

We can access the value at a specific index by using commas to get to that index.

const [a, b,,,c] = [1, 2, 3, 4, 5, 6];
console.log(a, b, c);
// Output is 1,2,5.

In the above example, a is index 0, b is index 1 and then each consecutive comma is the next index, that means that the first to third commas will be indexes 2 and three with the third comma, just before c will be index 4 which is the value of 5.

Destructuring & Rest Parameter:

In some situations involving array destructuring, we might want to collect the rest of the elements into a separate array.

const [a, b, ...arr] = [1, 2, 3, 4, 5, 7];
console.log(a, b);
console.log(arr);

Destructuring to pass objects as parameters:

In some cases, you can destructure the object in a function argument itself.

Let's have a look at the below:

const profileUpdate = (profileData) => {
  const { name, age, nationality, location } = profileData;
}

This effectively destructures the object sent into the function. This can also be done in-place:

const profileUpdate = ({ name, age, nationality, location }) => {}

Template Literals:

A new feature of ES6 is the template literal. This is a special type of string that makes creating complex strings easier.

Template literals allow you to create multi-line strings and to use string interpolation features to create strings.

const person = {
  name: "Zodiac Hasbro",
  age: 56
};

const greeting = `Hello, my name is ${person.name}!
I am ${person.age} years old.`;

console.log(greeting);

There are a few things going on in the above block of code:

  1. The above code uses backticks(```````) not quotes.
  2. The output is on more than one line both in the code and the console.
  3. The ${variable} is a placeholder. This eliminates the need for concatenation.

Defining object literals:

Let's look at the following:

const getMousePosition = (x, y) => ({
  x: x,
  y: y
});

ES6 helps us to reduce the redundancy of writing x:x. We can rewrite the code as below:

const getMousePosition = (x,y) => ({x,y});

Declarative Functions:

When defining functions within objects in ES5, we have to use the keyword function. With ES6, you can remove the function keyword and colon altogether when defining functions in objects. Here's an example of this syntax:

const person = {
  name: "Taylor",
  sayHello() {
    return `Hello! My name is ${this.name}.`;
  }
};

Classes:

ES6 provides a new syntax to create objects, using the class keyword.

It should be noted that the class syntax is just syntax, and not a full-fledged class-based implementation of an object-oriented paradigm, unlike in languages such as Java, Python, Ruby, etc.

In ES6, a class declaration has a constructor method that is invoked with the new keyword. If the constructor method is not explicitly defined, then it is implicitly defined with no arguments.

// Explicit constructor
class SpaceShuttle {
  constructor(targetPlanet) {
    this.targetPlanet = targetPlanet;
  }
  takeOff() {
    console.log("To " + this.targetPlanet + "!");
  }
}

// Implicit constructor 
class Rocket {
  launch() {
    console.log("To the moon!");
  }
}

const zeus = new SpaceShuttle('Jupiter');
// prints To Jupiter! in console
zeus.takeOff();

const atlas = new Rocket();
// prints To the moon! in console
atlas.launch();

NB UpperCamelCase should be used by convention for ES6 class names, as in SpaceShuttle used above.

Getters and Setters:

We can get the values from an object and set those values by using Getters and Setters.

  • Getters: Used to return the value of an object's private variables.
  • Setters: Used to modify the objects private variables.
class Book{
    constructor(author){
        this._author = author;
    }

    //getter
    get writer(){
        return this._author;
    }

    //setter
    set writer(updateAuthor){
        this._author = updateAuthor;
    }
}

const novel = new Book("Dale Carnegie");
console.log(novel.writer);
novel.writer = 'newAuthor';
console.log(novel.writer);

Scripts:

JavaScript started with a small role to play on an otherwise mostly HTML web. Today, it’s huge, and some websites are built almost entirely with JavaScript. In order to make JavaScript more modular, clean, and maintainable; ES6 introduced a way to easily share code among JavaScript files. This involves exporting parts of a file for use in one or more other files, and importing the parts you need, where you need them. In order to take advantage of this functionality, you need to create a script in your HTML document with a type of module.

<script type="module" src="filename.js"></script>

A script that uses this module type can now use the import and export features.

Exporting Methods:

Let's imagine we have a file called math.js. Within this file are a couple of methods we would like to use in other files. We need to export these methods in order for us to use them.

// math.js

// First Way:
export const add = (x,y) => {return x + y;}

// Second Way
const add = (x,y) => {return x + y;}
export {add};

We can export multiple methods by repeating the above or we can simplify it to the following:

export {add, subtract};

Importing Methods:

import allows you to choose which parts of a file or module to load. In the heading above, the examples exported add from the math.js file. Here's how you can import it to use in another file:

import {add} from './math.js';

We can import more than one method that get created in the same external file as follows:

import {add,subtract} from './math.js';

If you want to import everything within a specific file, we can do it as follows:

import * as myMathModule from './math.js';

The above will create an object called "myMathModule". We can then access the methods just like we would access the properties of an object.

myMathModule.add(2,3);
myMathModule.subtract(5,3);

Promises:

We use promises to make a promise to do something, usually asynchronously. When the task completes, you either fulfill your promise or fail to do so.

Promise is a constructor function, so you need to use the new keyword to create one. It takes a function, as its argument, with two parameters - resolve and reject. These are methods used to determine the outcome of the promise. The syntax looks like this:

const myPromise = new Promise((resolve, reject) => {

});

There are three states to a promise:

  1. pending: A promise stays in this state until it is completed.
  2. fulfilled: This is when a promise is done i.e. resolve.
  3. rejected: This is when a promise fails i.e. reject.
const myPromise = new Promise((resolve, reject) => {
  if(//condition here) {
    resolve("Promise was fulfilled");
  } else {
    reject("Promise was rejected");
  }
});

Promises are most useful when you have a process that takes an unknown amount of time in your code (i.e. something asynchronous), often a server request. When you make a server request it takes some amount of time, and after it completes you usually want to do something with the response from the server. This can be achieved by using the then method. The then method is executed immediately after your promise is fulfilled with resolve. Here’s an example:

const myPromise = new Promise((resolve, reject) => {
  if(//condition here) {
    resolve("Promise was fulfilled");
  } else {
    reject("Promise was rejected");
  }
});

myPromise.then(result => {
    console.log(result);
});

catch is the method used when your promise has been rejected. It is executed immediately after a promise's reject method is called.

const myPromise = new Promise((resolve, reject) => {
  if(//condition here) {
    resolve("Promise was fulfilled");
  } else {
    reject("Promise was rejected");
  }
});

myPromise.catch(result => {
    console.log(result);
});
⚠️ **GitHub.com Fallback** ⚠️