Skip to content

Javascript ES6

Irvine Sunday edited this page Jul 19, 2023 · 13 revisions

ECMAScript 2015 was the second major revision to JavaScript. It is also known as ECMAScript 6 or ES6.

New Features in ES6

Let Keyword

The let keyword allows you to declare a variable with block scope. Before ES6 (2015), JavaScript had Global Scope and Function Scope. With let, Variables declared inside a { } block cannot be accessed from outside the block

var x = 10;
// Here x is 10
{
  let x = 2;
  // Here x is 2
}
// Here x is 10

Variables defined with let cannot be Redeclared in the same block. Redeclaring a variable inside a block will not redeclare the variable outside the block:

let x = 10;
// Here x is 10

{
let x = 2;
// Here x is 2
}

// Here x is 10

Variables defined with let must be Declared before use. Variables defined with let are also hoisted to the top of the block, but not initialized. This means that using a let variable before it is declared will result in a ReferenceError:

Const Keyword

JavaScript const variables must be assigned a value when they are declared

const PI = 3.14159265359;

Variables defined with const cannot be Redeclared in the same scope. Redeclaring an existing var or let variable to const, in the same scope, is not allowed:

var x = 2;     // Allowed
const x = 2;   // Not allowed

{
let x = 2;     // Allowed
const x = 2;   // Not allowed
}

{
const x = 2;   // Allowed
const x = 2;   // Not allowed
x = 2;         // Not allowed
var x = 2;     // Not allowed
let x = 2;     // Not allowed
}

Variables defined with const cannot be Reassigned

const PI = 3.141592653589793;
PI = 3.14;      // This will give an error
PI = PI + 10;   // This will also give an error

Variables defined with const have Block Scope. Redeclaring a variable with const, in another scope, or in another block, is allowed:

const x = 10;
// Here x is 10

{
const x = 2;
// Here x is 2
}

// Here x is 10

Always declare a variable with const when you know that the value should not be changed. Use const when you declare:

  • A new Array
  • A new Object
  • A new Function
  • A new RegExp

For objects an arrays, the const keyword defines a constant reference to a value. Because of this, you can change the elements of a constant array and the properties of a constant object.

// You can create a constant array:
const cars = ["Saab", "Volvo", "BMW"];

// You can change an element:
cars[0] = "Toyota";

// You can add an element:
cars.push("Audi");

//But you can NOT reassign the array
cars = ["Toyota", "Volvo", "Audi"];    // ERROR
// You can create a const object:
const car = {type:"Fiat", model:"500", color:"white"};

// You can change a property:
car.color = "red";

// You can add a property:
car.owner = "Johnson";

// But you can NOT reassign the object
car = {type:"Volvo", model:"EX60", color:"red"};    // ERROR

Variables defined with const are also hoisted to the top, but not initialized. Meaning: Using a const variable before it is declared will result in a ReferenceError

alert (carName);
const carName = "Volvo";

Arrow Functions

Arrow functions allows a short syntax for writing function expressions.
You don't need the function keyword, the return keyword, and the curly brackets.
If you have parameters, you pass them inside the parentheses:

// ES5
var x = function(x, y) {
   return x * y;
}

// ES6
const x = (x, y) => x * y;

Arrow functions do not have their own this. They are not well suited for defining object methods.
Arrow functions are not hoisted. They must be defined before they are used.
Using const is safer than using var, because a function expression is always a constant value.
You can only omit the return keyword and the curly brackets if the function is a single statement. Because of this, it might be a good habit to always keep them:

const x = (x, y) => { return x * y };

In fact, if you have only one parameter, you can skip the parentheses as well

let hello = val => "Hello " + val;

In regular functions the this keyword represented the object that called the function, which could be the window, the document, a button or whatever. With arrow functions the this keyword always represents the object that defined the arrow function. With an arrow function this represents the owner of the function.

<button id="btn">Click Me!</button>

<p id="demo"></p>

<script>
let hello = "";

hello = function() {
  document.getElementById("demo").innerHTML += this;
}

//The window object calls the function:
window.addEventListener("load", hello);

//A button object calls the function:
document.getElementById("btn").addEventListener("click", hello);
</script>

returns two different objects (window and button)

<button id="btn">Click Me!</button>

<p id="demo"></p>

<script>
let hello = "";

hello = () => {
  document.getElementById("demo").innerHTML += this;
}

//The window object calls the function:
window.addEventListener("load", hello);

//A button object calls the function:
document.getElementById("btn").addEventListener("click", hello);
</script>

returns the window object twice, because the window object is the "owner" of the function.

Spread Operator

The ... operator expands an iterable (like an array) into more elements:

const q1 = ["Jan", "Feb", "Mar"];
const q2 = ["Apr", "May", "Jun"];
const q3 = ["Jul", "Aug", "Sep"];
const q4 = ["Oct", "Nov", "May"];

const year = [...q1, ...q2, ...q3, ...q4];
console.log(year)
// ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'May']

const numbers = [23,55,21,87,56];
let maxValue = Math.max(...numbers);
console.log(maxValue)
//87

For/ of

Lets you loop over data structures that are iterable such as Arrays, Strings, Maps, NodeLists, and more.
syntax:

for (variable of iterable) {
  // code block to be executed
}

variable - For every iteration the value of the next property is assigned to the variable. Variable can be declared with const, let, or var.
iterable - An object that has iterable properties.
Example:

// Looping over an Array
const cars = ["BMW", "Volvo", "Mini"];

for (let x of cars) {
  console.log(x)
}
/*
BMW
Volvo
Mini
*/

// Looping over a String
let language = "Java";

for (let x of language) {
    console.log(x)
}

/*
J
a
v
a
*/

JavaScript Maps

A Map is a built in data structure that allows you to store key-value pairs.
Maps are Objects. typeof returns object
Maps provide an efficient way to store and retrieve data based on keys and offer several advantages over regular JavaScript objects, such as:

  • Key flexibility: Unlike objects, Maps allow any data type to be used as keys, including objects, functions, and primitive types like strings, numbers, and symbols.
  • Order preservation: Maps preserve the insertion order of elements. When iterating over a Map, the elements will be returned in the order they were added.
  • Size tracking: Maps have a built-in property, size, that allows you to easily determine the number of key-value pairs present in the Map.
  • Built-in methods: Maps provide a set of methods to manipulate and retrieve data, such as set(), get(), has(), delete(), and clear().
// Creating a Map
const myMap = new Map();

// Adding or changing key-value pairs
myMap.set('name', 'John');
myMap.set('age', 25);
myMap.set({ id: 1 }, 'Some data');

// Getting the size of the Map
console.log(myMap.size); // Output: 3

// Retrieving values
console.log(myMap.get('name')); // Output: John
console.log(myMap.get('age')); // Output: 25

const objKey = { id: 1 };
console.log(myMap.get(objKey)); // Output: Some data

// Checking if a key exists
console.log(myMap.has('name')); // Output: true
console.log(myMap.has('occupation')); // Output: false

// Iterating over the Map keys
for (const key of myMap.keys()) {
  console.log(key);
}
/* Output:
   name
   age
   { id: 1 }
*/

// Iterating over the Map values
for (const value of myMap.values()) {
  console.log(value);
}
/* Output:
   John
   25
   some data
*/

// Iterating over the Map entries
for (const [key, value] of myMap.entries()) {
  console.log(`${key}: ${value}`);
}
/* Output:
   name: John
   age: 25
   [object Object] : some data
*/

// Iterating over the Map using forEach
myMap.forEach((value, key) => {
  console.log(`${key}: ${value}`);
});
/* Output:
   name: John
   age: 25
   [object Object] : some data
*/

// Deleting a key-value pair
myMap.delete('age');
console.log(myMap.has('age')); // Output: false

// Clearing the Map
myMap.clear();
console.log(myMap.size); // Output: 0

You can create a Map by passing an Array to the new Map() constructor:

// Create a Map
const fruits = new Map([
  ["apples", 500],
  ["bananas", 300],
  ["oranges", 200]
]);

JavaScript Objects vs Maps

Object Map
Not directly iterable Directly iterable
Do not have a size property Have a size property
Keys must be Strings (or Symbols) Keys can be any datatype
Keys are not well ordered Keys are ordered by insertion
Have default keys Do not have default keys

JavaScript Sets

A JavaScript Set is a collection of unique values. Each value can only occur once in a Set.
A Set can hold any value of any data type.
Unlike arrays, sets do not maintain any specific order of elements. They provide methods for adding, removing, and checking the presence of elements.
Size property returns the numbber of elements in a set.
For a Set, typeof returns object:

// Creating a new Set
const letters = new Set(["a","b","c"]);
const mySet = new Set();

// Adding elements to the Set
mySet.add(1);
mySet.add("Hello");
mySet.add(true);

console.log(mySet); // Output: Set { 1, "Hello", true }

// Checking the size of the Set
console.log(mySet.size); // Output: 3

// Checking if an element exists in the Set
console.log(mySet.has("Hello")); // Output: true
console.log(mySet.has(false)); // Output: false

// Using values() or keys() to iterate over the elements of the Set:
for (const value of mySet.values()) {
  console.log(value);
}
// Output:
// 1
// "Hello"
// true

for (const key of mySet.keys()) {
  console.log(key);
}
// Output:
// 1
// "Hello"
// true

Using entries() to iterate over the [value, value] pairs of the Set:
for (const entry of mySet.entries()) {
  console.log(entry);
}
// Output:
// [1, 1]
// ["Hello", "Hello"]
// [true, true]

// Removing an element from the Set
mySet.delete(1);

// Iterating over the Set
mySet.forEach((value) => {
  console.log(value);
});

mySet.clear();
console.log(mySet); // Output: Set {}

JavaScript Classes

JavaScript Classes are templates for JavaScript Objects.
Use the keyword class to create a class.
Always add a method named constructor()
A JavaScript class is not an object.

class Animal {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  // Method defined inside the class
  eat() {
    console.log(`${this.name} is eating.`);
  }

  // Another method defined inside the class
  sleep() {
    console.log(`${this.name} is sleeping.`);
  }
}

// Creating an instance of the Animal class
const dog = new Animal("Buddy", 3);

console.log(dog.name); // Output: Buddy
console.log(dog.age); // Output: 3

dog.eat(); // Output: Buddy is eating.
dog.sleep(); // Output: Buddy is sleeping.

The constructor method is a special method:

  • It has to have the exact name "constructor"
  • It is executed automatically when a new object is created
  • It is used to initialize object properties
    If you do not define a constructor method, JavaScript will add an empty constructor method.

JavaScript Promises

A promise is an object that represents the eventual completion or failure of an asynchronous operation.
A Promise is a proxy for a value not necessarily known when the promise is created.
This lets asynchronous methods return values like synchronous methods: instead of immediately returning the final value, the asynchronous method returns a promise to supply the value at some point in the future.
A Promise is a JavaScript object that links "Producing Code" and "Consuming Code".
"Producing Code" can take some time and "Consuming Code" must wait for the result.

const myPromise = new Promise(function(myResolve, myReject) {
// "Producing Code" (May take some time)

  myResolve(); // when successful
  myReject();  // when error
});

// "Consuming Code" (Must wait for a fulfilled Promise).
myPromise.then(
  function(value) { /* code if successful */ },
  function(error) { /* code if some error */ }
);

A Promise can be in one of three states:

  • Pending: The initial state of a Promise before it is settled, i.e., before it is resolved or rejected.
  • Fulfilled: The Promise is resolved, meaning the operation completed successfully.
  • Rejected: The Promise is rejected, indicating that an error occurred during the operation.
    The Promise object supports two properties: state and result.
    While a Promise object is "pending" (working), the result is undefined. When a Promise object is "fulfilled", the result is a value.
    When a Promise object is "rejected", the result is an error object.
    The eventual state of a pending promise can either be fulfilled with a value or rejected with a reason (error). When either of these options occur, the associated handlers queued up by a promise's then method are called.
    Promise.then() takes two arguments, a callback for success and another for failure. Both are optional, so you can add a callback for success or failure only.
// Creating a Promise
const myPromise = new Promise((resolve, reject) => {
  // Simulating an asynchronous operation
  setTimeout(() => {
    const randomNumber = Math.random();
    if (randomNumber < 0.5) {
      // Resolving the Promise
      resolve(randomNumber);
    } else {
      // Rejecting the Promise
      reject(new Error("Random number is too high!"));
    }
  }, 2000);
});

// Consuming the Promise
myPromise.then(
  function(result) {
    console.log("Promise resolved with result:", result);
  },
  function(error) {
    console.log("Promise rejected with error:", error.message);
  }
);

The promise constructor takes one argument, a callback with two parameters, resolve and reject.
Do something within the callback, perhaps async, then call resolve if everything worked, otherwise call reject.
it's customary, but not required, to reject with an Error object. The benefit of Error objects is they capture a stack trace, making debugging tools more helpful.
then() takes two arguments, a callback for a success case, and another for the failure case. Both are optional, so you can add a callback for the success or failure case only.
Imagine you have a web application that needs to fetch data from an API and display it on the webpage. In this case, you can use Promises to handle the asynchronous nature of the API request and handle the data once it's received.
Let's use the Chuck Norris API, which provides random Chuck Norris jokes. It's a free API that doesn't require any authentication.

// Fetching a random Chuck Norris joke from the API
const fetchChuckNorrisJoke = () => {
  return new Promise((resolve, reject) => {
    fetch('https://api.chucknorris.io/jokes/random')
      .then(response => {
        if (!response.ok) {
          throw new Error('Failed to fetch Chuck Norris joke');
        }
        return response.json();
      })
      .then(data => resolve(data.value))
      .catch(error => reject(error));
  });
};

// Consuming the Promise
fetchChuckNorrisJoke()
  .then(joke => {
    // Display the Chuck Norris joke
    console.log(joke);
  })
  .catch(error => {
    // Handle any errors that occurred during the API request
    console.log('Error:', error.message);
  });

In this example, we define the fetchChuckNorrisJoke function, which creates a new Promise and uses the fetch function to make a GET request to the Chuck Norris API's /jokes/random endpoint.
Inside the Promise, we check if the response is successful (response.ok) and convert the response body to JSON using response.json(). If everything is successful, we resolve the Promise with the value of the joke (data.value).
If there's an error during the API request or JSON parsing, we catch the error and reject the Promise.
When consuming the Promise, we chain a .then() method to handle the resolved state. Inside the resolved callback function, we have access to the Chuck Norris joke, and in this case, we log it to the console.
If there's an error during the API request, we catch the error using the .catch() method and log the error message.

Promise Chaining

// Simulating an asynchronous operation that fetches user data
function fetchUserData() {
  return new Promise((resolve) => {
    setTimeout(() => {
      const userData = { id: 1, name: "John Doe", email: "[email protected]" };
      resolve(userData);
    }, 1000);
  });
}

// Simulating another asynchronous operation that fetches user orders
function fetchUserOrders(userId) {
  return new Promise((resolve) => {
    setTimeout(() => {
      const userOrders = [
        { id: 101, product: "Phone", price: 599.99 },
        { id: 102, product: "Laptop", price: 1299.99 },
      ];
      resolve(userOrders);
    }, 1500);
  });
}

// Chaining the Promises to fetch user data and then their orders
fetchUserData()
  .then((userData) => {
    console.log("User Data:", userData);
    return fetchUserOrders(userData.id); // Returning a new Promise for chaining
  })
  .then((userOrders) => {
    console.log("User Orders:", userOrders);
    // You can continue chaining more Promises here if needed
  })
  .catch((error) => {
    console.log("Error:", error.message);
  });

In this example, we have two asynchronous functions: fetchUserData() and fetchUserOrders(userId). The fetchUserData() function returns a Promise that resolves with some user data, and the fetchUserOrders(userId) function takes a user ID as input and returns a Promise that resolves with the user's orders.

The Symbol Type

A JavaScript Symbol is a primitive datatype just like Number, String, or Boolean.
Symbols are unique and immutable values that can be used as property keys in objects. Each Symbol value is unique and can be used to avoid property name clashes when defining object properties.
For instance, if different coders want to add a person.id property to a person object belonging to a third-party code, they could mix each others values. Using Symbol() to create a unique identifiers, solves this problem:

const person = {
  firstName: "John",
  lastName: "Doe",
  age: 50,
  eyeColor: "blue"
};

let id = Symbol('id');
person[id] = 140353;
// Now person[id] = 140353
// but person.id is still undefined

If you create two symbols with the same description they will have different values:

Symbol("id") == Symbol("id"); // false
// Creating a Symbol
const mySymbol = Symbol();

// Using a Symbol as a property key
const obj = {};
obj[mySymbol] = "Hello";

console.log(obj[mySymbol]); // Output: Hello
console.log(obj); // Output: { [Symbol()]: 'Hello' }

// Retrieving all symbol properties from an object
const symbolKeys = Object.getOwnPropertySymbols(obj);
console.log(symbolKeys); // Output: [ Symbol() ]

When printing the object obj, the Symbol key [Symbol()] is shown, indicating the presence of a Symbol property. However, the Symbol itself is not directly accessible or readable.
To retrieve all Symbol properties from an object, you can use the Object.getOwnPropertySymbols() method, which returns an array of all Symbol keys associated with the object.
Symbols have a few important characteristics:

  • Symbols are unique: Each Symbol value is unique, and two Symbols are never equal, even if they have the same description.
  • Symbols are not enumerable: Symbols are not included in regular object enumerations using for...in or Object.keys(). This helps in hiding Symbol properties from unintentional iteration.
  • Symbols can have descriptions: Symbols can optionally be created with a description string as an argument, which can be helpful for debugging or identifying the purpose of a Symbol.

Default Parameter Values

. If a function is called without providing a value for a parameter or if the value passed is undefined, the default value assigned to that parameter will be used instead.

function myFunction(x, y = 10) {
  // y is 10 if not passed or undefined
  return x + y;
}
myFunction(5); // will return 15

Default parameter values are useful when you want to handle cases where a value is not provided or when you want to provide a fallback value.

Function Rest Parameter

The rest parameter in JavaScript allows a function to accept an indefinite number of arguments as an array.
It is denoted by three dots (...) followed by a parameter name. The rest parameter collects all the remaining arguments passed to the function into an array.

function sum(...numbers) {
  let total = 0;
  for (let number of numbers) {
    total += number;
  }
  return total;
}

console.log(sum(1, 2, 3)); // Output: 6
console.log(sum(4, 5, 6, 7, 8)); // Output: 30

The rest parameter is useful when you want to write functions that can accept a variable number of arguments and process them as an array. It provides flexibility and simplifies handling functions with varying numbers of inputs.

String.startsWith()

The startsWith() method returns true if a string begins with a specified value, otherwise false:

// syntax
string.startsWith(searchString[, position]);

const str = "Hello, world!";

console.log(str.startsWith("Hello")); // Output: true
console.log(str.startsWith("ello")); // Output: false
console.log(str.startsWith("Hello", 6)); // Output: false (starting position is 6)
console.log(str.startsWith("world", 7)); // Output: true (starting position is 7)

String.endsWith()

The endsWith() method returns true if a string ends with a specified value, otherwise false:

// syntax
string.endsWith(searchString[, length]);

const str = "Hello, world!";

console.log(str.endsWith("world!")); // Output: true
console.log(str.endsWith("world")); // Output: false
console.log(str.endsWith("Hello", 6)); // Output: true (considering the first 6 characters)
console.log(str.endsWith("Hello", 5)); // Output: false (considering the first 5 characters)

String.includes()

The includes() method returns true if a string contains a specified value, otherwise false:

// syntax
string.includes(searchString[, position]);

const str = "Hello, world!";

console.log(str.includes("Hello")); // Output: true
console.log(str.includes("hello")); // Output: false (case-sensitive)
console.log(str.includes("world")); // Output: true
console.log(str.includes("world", 7)); // Output: false (starting position is 7)

Array.from()

The Array.from() method returns an Array object from any object with a length property or any iterable object.
It allows you to convert iterable or array-like objects into actual arrays.

//  syntax
Array.from(iterable[, mapFn[, thisArg]]);

// Converting a string into an array
const str = "Hello";
const strArray = Array.from(str);
console.log(strArray); // Output: ["H", "e", "l", "l", "o"]

// Converting a Set into an array
const mySet = new Set([1, 2, 3]);
const setArray = Array.from(mySet);
console.log(setArray); // Output: [1, 2, 3]

// Converting an array-like object (arguments) into an array
function convertToArray() {
  const argsArray = Array.from(arguments);
  console.log(argsArray);
}

convertToArray("Hello", 1, true); // Output: ["Hello", 1, true]

iterable: An iterable object or an array-like object to be converted into an array. mapFn (optional): A mapping function to be applied to each element of the iterable. It allows you to transform the elements before they are added to the new array. thisArg (optional): A value to be used as this when executing the mapFn function.

Array keys()

The keys() method returns an Array Iterator object with the keys of an array.

const fruits = ["Banana", "Orange", "Apple", "Mango"];
const keysIterator = fruits.keys();

for (let key of keysIterator) {
  console.log(key);
}

// output
0
1
2
3

Modules

In JavaScript, modules are a way to organize and structure your code by dividing it into separate files, each focusing on a specific functionality or feature. Modules allow you to encapsulate related code, making it more maintainable, reusable, and easier to understand.
Modules are imported from external files with the import statement.
Modules also rely on type="module" in the <script> tag.

<script type="module">
import message from "./message.js";
</script>

Modules with functions or variables can be stored in any external file.
There are two types of exports: Named Exports and Default Exports.

Named Exports

You can create named exports two ways. In-line individually, or all at once at the bottom.

// person.js

export const name = "Jesse";
export const age = 40;

// or 

const name = "Jesse";
const age = 40;

export {name, age};
Default Exports

You can only have one default export in a file.

// message.js

const message = () => {
const name = "Jesse";
const age = 40;
return name + ' is ' + age + 'years old.';
};

export default message;
Import

You can import modules into a file in two ways, based on if they are named exports or default exports.
Named exports are constructed using curly braces. Default exports are not.

// Import named exports from the file person.js:

import { name, age } from "./person.js";

// Import a default export from the file message.js:

import message from "./message.js";

Modules only work with the HTTP(s) protocol. A web-page opened via the file:// protocol cannot use import / export.

New Math methods

  • Math.trunc(x): Returns the integer part of a number x by removing any fractional digits without rounding. It essentially truncates the decimal portion and returns the whole number.
  • Math.sign(x): Returns the sign of a number x as 1 (positive), -1 (negative), or 0 (zero). It indicates whether the number is positive, negative, or zero, without changing its sign.
  • Math.cbrt(x): Returns the cube root of a number x. It calculates the value that, when multiplied by itself three times, gives x.
  • Math.log2(x): Returns the base 2 logarithm of a positive number x. It calculates the exponent to which the base 2 must be raised to obtain x.
  • Math.log10(x): Returns the base 10 logarithm of a positive number x. It calculates the exponent to which the base 10 must be raised to obtain x.
console.log(Math.trunc(3.8)); // Output: 3
console.log(Math.sign(-7)); // Output: -1
console.log(Math.cbrt(27)); // Output: 3
console.log(Math.log2(8)); // Output: 3
console.log(Math.log10(100)); // Output: 2

Object entries()

The Object.entries() method is a built-in function in JavaScript that returns an array of key-value pairs for the enumerable properties of an object. The order of the key-value pairs in the resulting array is the same as the order in which they would be iterated by a for...in loop.

const fruits = ["Banana", "Orange", "Apple", "Mango"];
const f = fruits.entries();

for (let entry of f) {
  console.log(entry);
}

// output
[0, "Banana"]
[1, "Orange"]
[2, "Apple"]
[3, "Mango"]
Clone this wiki locally