What the difference between ES5 and ES6?

What the difference between ES5 and ES6?

Let’s take a look at the main differences features between ECMAScript 5 (ES5) and ECMAScript 6 (ES6) versions of JavaScript

Introduction

ECMAScript (ES) is a standardised scripting language for JavaScript (JS). The current ES version supported in modern browsers is ES5. However, ES6 tackles a lot of the limitations of the core language, making it easier for devs to code.

The most common and important standards that are being used in the software development industry are ES5 (also known as ECMAScript 2009) and ES6 (also known as ECMAScript 2015).

In this article, we will understand the different features of ES5 and ES6 versions of JavaScript. We will discuss problems that were found in ECMAScript 2009 and changes that have been made in ECMAScript 2015 to overcome the limitations of ES5 and further improve JavaScript language.

1. The const keyword

The const keyword of ES6 is used to declare the read only variables, whose values cannot be changed.

<!DOCTYPE html>      
<html lang="en">      
   <head>      
      <script type="text/javascript">      
         const pi = 3.14;      
               
         var r = 4;      
               
         console.log(pi * r * r); //output "50.24"      
               
         pi = 12;      
               
      </script>      
      <meta charset="UTF-8">      
      <title>Document</title>      
   </head>      
   <body>      
   </body>      
</html>    

It throws an Uncaught TypeError: Assignment to constant variable, because variables declared using const keyword cannot be modified.

2. The var vs let keyword

The var Keyword

In ECMAScript 5 and earlier versions of JavaScript, the var statement is used to declare variables.

Here is an example that demonstrates how to create function-scoped variables:

<!DOCTYPE html>    
<html>    
   <head>    
      <script>    
         var x = 11; // can be accessed globally    
         function myFunction()    
         {    
         console.log(x);    
         var y = 12; //can be accessed throughout function    
         if(true)    
         {    
         var z = 13; //can be accessed throughout function    
         console.log(y);    
         }    
         console.log(z);    
         }    
             
         myFunction();    
             
      </script>    
   </head>    
   <body></body>    
</html>   

Here, as you can see, the z variable is accessible outside the if statement, but that is not the case with other programming languages. Programmers coming from other language backgrounds would expect the z variable to be undefined outside the if statement, but that's not the case. Therefore, ES6 had introduced the let keyword, which can be used for creating variables that are block scoped.

The let keyword

The let keyword is used for declaring block scoped variables. As earlier versions of JavaScript do not support block scoped variables, and nearly each and every programming language supports block scoped variables due to this limitation, the let keyword is introduced in ES6.

Variables declared using the let keyword are called block scoped variables. When the block scoped variables are declared inside a block, then they are accessible inside the block that they are defined in (and also any sub-blocks) but not outside the block.

<!DOCTYPE html>    
<html>    
   <head>    
      <script>    
         let x = 13; //can be accessed globally    
         function myFunction()    
         {    
         console.log(x);    
         let y = 14; //can be accessed throughout function    
         if(true)    
         {    
         let z = 14; //cannot be accessed outside of the "if" statement    
         console.log(y);    
         }    
         console.log(z);    
         }    
         myFunction();    
             
             
      </script>    
   </head>    
   <body>    
   </body>    
</html>  

The above code throws Uncaught ReferenceError: z is not defined

Now, the output is as expected by a programmer who is used to another programming language. Here variables are declared using let keyword inside the {} block and cannot be accessed from outside.

3. The arrow functions

In ECMAScript 5, JavaScript function is defined using function keyword followed by name and parentheses ().

Here is an example,

<!DOCTYPE html>    
<html lang="en">    
   <head>    
      <script type="text/javascript">    
         const add1 = function(a,b)    
         {    
         return a+b;    
         }    
         console.log(add1(60,20));    
      </script>    
      <meta charset="UTF-8">    
      <title>Document</title>    
   </head>    
   <body></body>    
</html>  

In ECMAScript 6, functions can be created using => operator. These functions with => operator are called arrow functions. With arrow functions, we can write shorter function syntax. If function is having only one statement and that statement is returning a value, then the brackets {} and return keyword can be removed from the function definition.

<!DOCTYPE html>    
<html lang="en">    
   <head>    
      <script type="text/javascript">    
         const add1 = (a,b) => a+b;    
         console.log(add1(50,20));    
      </script>    
      <meta charset="UTF-8">    
      <title>Document</title>    
   </head>    
   <body>    
   </body>    
</html>   
4. Object oriented programming concept

Earlier versions of JavaScript did not have true object inheritance, they had prototype inheritance. Therefore ES6 has provided a feature called classes to help us organize our code in a better way and to make it more legible.

Object creation in ES5 version of JavaScript is as follows,

<!DOCTYPE html>    
<html lang="en">    
   <head>    
      <script>    
         function Car(options){    
         this.title = options.title;    
         }    
         Car.prototype.drive = function(){    
         return 'vroom';    
         };    
         function Toyota(options){    
         Car.call(this,options);    
         this.color = options.color;    
         }    
         Toyota.prototype = Object.create(Car.prototype);    
         ToyotaToyota.prototype.constructor = Toyota;    
         Toyota.prototype.honk = function(){    
         return 'beep';    
         }    
         const toyota = new Toyota({color:"red",title:"Daily Driver"});    
         document.getElementById("demo").innerHTML = toyota.drive();    
         document.getElementById("demo1").innerHTML = toyota.title;    
         document.getElementById("demo2").innerHTML = toyota.honk();    
         document.getElementById("demo3").innerHTML = toyota.color;    
      </script>    
      <meta charset="UTF-8">    
      <title>Document</title>    
   </head>    
   <body>    
      <p id="demo"></p>    
      <p id="demo1"></p>    
      <p id="demo2"></p>    
      <p id="demo3"></p>    
   </body>    
</html>   

From the above code, we can see that constructor object creation and prototype inheritance is very complicated.

Therefore to organize our code in a better way and to make it much easier to read and understand, ES6 has provided a feature called class. We can use classes for setting up some level of inheritance as well as to create objects to make code more legible.

5. Using class declaration for object creation and inheritance in ES6 version of JavaScript

Declare a class using class keyword and add constructor() method to it.

<!DOCTYPE html>    
<html>    
   <head>    
      <script>    
         class Car {    
         constructor(brand)    
         {    
         this.title=brand;    
         }    
         drive() {    
         return 'i have a '+this.title;    
         }    
         }    
         class Toyota extends Car{    
         constructor(brand,color){    
         super(brand);    
         this.color=color;    
         }    
         honk()    
         {    
         return this.drive()+',its a '+this.color;    
         }    
         }    
         const toyota = new Toyota("Ford","red");    
         document.getElementById("demo2").innerHTML = toyota.honk();    
      </script>    
      <meta charset="UTF-8">    
      <title>Document</title>    
   </head>    
   <body>    
      <h2>JavaScript Class</h2>    
      <p id="demo"></p>    
      <p id="demo1"></p>    
      <p id="demo2"></p>    
      <p id="demo3"></p>    
      <p id="demo4"></p>    
   </body>    
</html>   

Above code defines a class named ‘Car’ which is having its own constructor and method. Another class ‘Toyota’ is inheriting ‘Car’ constructor and method with the help of extends keyword.

6. Improved Array capabilities

While ECMAScript 5 introduced lots of methods in order to make arrays easier to use, ECMAScript 6 has added lot more functionality to improve array performance such as new methods for creating arrays and functionality to create typed arrays.

In ECMAScript 5 and earlier versions of JavaScript, there were two ways to create arrays such as creating arrays using array constructor and array literal syntax.

Creating arrays using array constructor,

<!DOCTYPE html>    
<html lang="en">    
   <head>    
      <script type="text/javascript">    
         let items = new Array(2);    
         console.log(items.length); // 2    
         console.log(items[0]); // undefined    
         console.log(items[1]); // undefined    
              
         items1 = new Array("2");    
         console.log(items1.length); // 1    
         console.log(items1[0]); // "2"    
              
         items2 = new Array(1, 2);    
         console.log(items2.length); // 2    
         console.log(items2[0]); // 1    
         console.log(items2[1]); // 2    
              
         items3 = new Array(3, "2");    
         console.log(items3.length); // 2    
         console.log(items3[0]); // 3    
         console.log(items3[1]);    
      </script>    
      <meta charset="UTF-8">    
      <title>Document</title>    
   </head>    
   <body></body>    
</html>  

As we can see from the above example, on passing a single numerical value to the array constructor, the length property of that array is set to that value and it would be an empty array. On passing a single non-numeric value, the length property is set to one. If multiple values are being passed (whether numeric or not) to the array, those values become items in the array.

This behavior is confusing and risky, because we might not always be aware of the type of data being passed.

Solution provided in ECMAScript 6 version of JavaScript is as follows,

7. The Array.of() method

In order to solve the problem of a single argument being passed in an array constructor, ECMAScript 6 version of JavaScript introduced the Array.of() method.

The Array.of() method is similar to array constructor approach for creating arrays but unlike array constructor, it has no special case regarding single numerical value. No matter how many arguments are provided to the Array.of() method, it will always create an array of those arguments.

Here is an example showing the use of Array.of() method,

<!DOCTYPE html>    
<html lang="en">    
   <head>    
      <script type="text/javascript">    
         let items4 = Array.of(1, 2);    
         console.log(items4.length); // 2    
         console.log(items4[0]); // 1    
         console.log(items4[1]); // 2    
         items5 = Array.of(2);    
         console.log(items5.length); // 1    
         console.log(items5[0]); // 2    
         Items6 = Array.of("2");    
         console.log(items6.length); // 1    
         console.log(items6[0]); // "2"    
      </script>    
      <meta charset="UTF-8">    
      <title>Document</title>    
   </head>    
   <body></body>    
</html>  
8. Converting non-array objects into actual arrays

Converting a non array object to an array has always been complicated, prior to ECMAScript 6 version of JavaScript. For instance, if we are having an arguments object and we want to use it like an array, then we have to convert it to an array first.

In ECMAScript 5 version of JavaScript, in order to convert an array like object to an array, we have to write a function like in the below code,

<!DOCTYPE html>    
<html lang="en">    
   <head>    
      <script type="text/javascript">    
         function createArray(arr) {    
         var result = [];    
         for (var i = 0, len = arr.length; i < len; i++) {    
         result.push(arr[i]);    
         }    
         return result;    
         }    
         var args = createArray("ABCDEFGHIJKL");    
         console.log(args);    
      </script>    
      <meta charset="UTF-8">    
      <title>Document</title>    
   </head>    
   <body></body>    
</html>   

Even though this approach of converting an array like object to an array works fine, it is taking a lot of code to perform a simple task.

9. The Array.from() method

Therefore in order to reduce the amount of code, ECMAScript 6 version of JavaScript introduced Array.from() method. The Array.from() method creates a new array on the basis of items provided as arguments during function call.

For example let's consider the following example

<!DOCTYPE html>    
<html lang="en">    
   <head>    
      <script type="text/javascript">    
         var arg = Array.from("ABCDEFGHIJKLMNO");    
         console.log(arg);    
      </script>    
      <meta charset="UTF-8">    
      <title>Document</title>    
   </head>    
   <body></body>    
</html>   

In the above code, the Array.from() method creates an array from string provided as an argument.

10. New methods for array

Before ECMAScript 5, searching in an array was a complicated process because there were no built in functions provided to perform this task. Therefore ECMAScript 5 introduced indexOf() and lastIndexOf() methods to search for a particular value in an array.

The indexOf() method,

<!DOCTYPE html>    
<html>    
   <head>    
      <script>    
         var languages = ["C#", "JavaScript", "Java", "Python"];    
         var x = languages.indexOf("Java");    
         console.log(x);     
             
      </script>    
   </head>    
   <body>    
      <p id="demo"></p>    
   </body>    
</html>   

The lastIndexOf() methods,

<!DOCTYPE html>    
<html>    
   <head>    
      <script>    
         var languages = ["C#", "JavaScript", "Java", "Python","C#"];    
         var x = languages.lastIndexOf("C#");    
         console.log(x);     
             
      </script>    
   </head>    
   <body>    
      <p id="demo"></p>    
   </body>    
</html>  

These two methods work fine, but their functionality was still limited as they can search for only one value at a time and just return their position.

Suppose, we want to find the first even number in an array of numbers, then we have to write our own code as there are no inbuilt functions available to perform this task in the ECMAScript 5 version of JavaScript. The ECMAScript 6 version of JavaScript solved this problem by introducing find() and findIndex() methods.

11. The Array.find() and Array.findIndex() function

The array.find() method returns the value of first array element which satisfies the given test (provided as a function). The find() method takes two arguments as follows:-

Array.find (function(value, index, array),thisValue).

The Array.findIndex() method is similar to the find() method. The findIndex() method returns the index of the satisfied array element.

<!DOCTYPE html>    
<html lang="en">    
   <head>    
      <script type="text/javascript">    
         let numbers = [25, 30, 35, 40, 45];    
         console.log(numbers.find(n => n % 2 ==0 ));    
         console.log(numbers.findIndex(n => n % 2 ==0));    
      </script>    
      <meta charset="UTF-8">    
      <title>Document</title>    
   </head>    
   <body></body>    
</html>  
12. Sets and Maps

In ES5, sets and maps objects can be created using Object.create() as follows,

<!DOCTYPE html>    
<html lang="en">    
   <head>    
      <script type="text/javascript">    
         var set = Object.create(null);    
         set.firstname = "Krysten";    
         set.lastname = "Calvin";    
             
         console.log(set.firstname);    
         console.log(set.lastname);    
      </script>    
      <meta charset="UTF-8">    
      <title>Document</title>    
   </head>    
   <body></body>    
</html>  

In this example, the object set is having a null prototype, to ensure that the object does not have any inherited properties.

Using objects as sets and maps works fine in simple situations. But this approach can cause problems, when we want to use strings and numbers as keys because all object properties are strings by default, therefore strings and numbers will be treated as same property, and two keys cannot evaluate the same string.

For example, let's consider the following code,

<!DOCTYPE html>    
<html lang="en">    
   <head>    
      <script type="text/javascript">    
         var map = Object.create(null);    
         map[2] = "hello";    
         console.log(map["2"]);     
      </script>    
      <meta charset="UTF-8">    
      <title>Document</title>    
   </head>    
   <body>    
   </body>    
</html>  

In this example, the numeric key of 2 is assigned a string value “hello" as all object properties are strings by default. The numeric value 2 is converted to a string, therefore  map["2"] and map[2] actually reference the same property.

13. ECMAScript 6 introduced sets and maps in JavaScript

Sets

A set is an ordered list of elements that cannot contain duplicate values. Set object is created using new Set(), and items are added to a set by calling the add() method.

You can see how many items are in a set by checking the size property,

Lets consider the following code,

<!DOCTYPE html>    
<html lang="en">    
   <head>    
      <script type="text/javascript">    
         let set = new Set();    
         set.add(10);    
         set.add(20);    
         set.add(30);    
         set.add(40);    
         console.log(set);    
         console.log(set.size);     
             
      </script>    
      <meta charset="UTF-8">    
      <title>Document</title>    
   </head>    
   <body>    
   </body>    
</html>   

Here size property returns the number of elements in the Set. And add method adds the new element with a specified value at the end of the Set object.

Maps

A map is a collection of elements that contains elements as key, value pair. Each value can be retrieved by specifying the key for that value. The ECMAScript 6 Map type is an ordered list of key-value pairs, where the key and the value can be any type.

<!DOCTYPE html>    
<html lang="en">    
   <head>    
      <script type="text/javascript">    
         let map = new Map();    
         map.set("firstname", "Shan");    
         map.set("lastname", "Tavera");    
         console.log(map.get("firstname"));     
         console.log(map.get("lastname"));     
             
      </script>    
      <meta charset="UTF-8">    
      <title>Document</title>    
   </head>    
   <body></body>    
</html>  
14. Functions with default parameters in JavaScript

JavaScript functions have different functionalities in comparison to other programming languages such that any number of parameters are allowed to be passed in spite of the number of parameters declared in function definition. Therefore, functions can be defined that are allowed to handle any number of parameters just by passing in default values when there are no parameters provided.

Default parameters in ECMAScript 5

Now we are going to discuss, how function default parameters would be handled in ECMAScript 5 and earlier versions of ECMAScipt. JavaScript does not support the concept of default parameters in ECMAScript 5, but with the help of logical OR operator (||) inside function definition, the concept of default parameters can be applied.

To create functions with default parameters in ECMAScipt 5, the following syntax would be used,

<!DOCTYPE html>    
<html>    
   <body>    
      <script type="text/javascript">    
         function calculate(a, b) {    
         aa = a || 30;    
         bb = b || 40;    
         console.log(a, b);    
         }    
         calculate(10,20); // 10 20    
         calculate(10); // 10 40    
         calculate(); // 30 40    
      </script>    
   </body>    
</html>  

Inside function definition, missing arguments are automatically set to undefined. We can use logical OR (||) operator in order to detect missing values and set default values. The OR operator looks for the first argument, if it is true, the operator returns it, and if not, the operator returns the second argument.

Default parameters in ECMAScript 6

ECMAScript 6, allows default parameter values to be put inside the function declaration. Here we are initializing default values inside function declaration which is used when parameters are not provided during function calling.

<!DOCTYPE html>  
<html>  
<body>  
   <script type="text/javascript">  
      function calculate(a=30, b=40) {  
         console.log(a, b);  
      }  
      calculate(10,20); // 10 20  
      calculate(10); // 10 40  
      calculate(); // 30 40  
   </script>  
</body>  
</html>  
Summary

In this article, we have discussed briefly, the differences between ES5 and ES6 versions of JavaScript as well as some of the new features introduced in each of these versions. Each and every feature has been explained with proper coding examples.

I hope this tutorial will surely help and you if you liked this tutorial, please consider sharing it with others. Thank you so much!

JavaScript ES6 Classes

JavaScript ES6 Classes

An exciting new construct that was introduced in the ES6 specification is the ES6 classes. If you're a Javascript developer, you will be aware that Javascript follows prototypal inheritance and sometimes it can get a little messy. However, with ES6 classes the syntax is simpler and much more intuitive.

An exciting new construct that was introduced in the ES6 specification is the ES6 classes. If you're a Javascript developer, you will be aware that Javascript follows prototypal inheritance and sometimes it can get a little messy. However, with ES6 classes the syntax is simpler and much more intuitive.

Classes are a fundamental part of object oriented programming (OOP). They define “blueprints” for real-world object modeling and organize code into logical, reusable parts. Classes share many similarities with regular objects. They have their own constructor functions, properties, and methods. In this tutorial, we’ll demonstrate how to create and work with ES6 classes.

Creating a class

You can create a class using the class keyword:

class Person{

  constructor(name, age) {

    this.name = name

    this.age = age

  }

}



let person = new Person("Sam", 30)



person.name

//returns 'Sam'



person.age

//returns 30

Notice how we define our Person class with a constructor() function taking two arguments name and age. Using the this keyword, we set the name and age properties based on the provided arguments. Remember that the constructor function is called whenever we create a new instance of the Person class.

Similar to objects, we can read class properties using dot notation so that person.name returns Sam.

Defining properties and methods

You can define properties and methods for a class the same way you do for regular objects:

class Person{

  constructor(name, age) {

    this.name = name

    this.age = age

  }



  sayName() {

    console.log("My name is " + this.name)

  }

}



let person = new Person('Tim', 40)



person.sayName()

//logs 'My name is Tim'



person.location = 'London'



person.location

//returns 'London'

Notice how we define a sayName() function within our Person class definition. Once we create a new instance of Person, we can call the method via person.sayName().

You can also add properties and methods on the fly. You’ll notice that while the location property isn’t defined in our constructor function, we can still dynamically add it later on for the person instance. Remember that if we created a new instance of Person, it would not have a location property because that property is not defined in the class definition. Only properties and methods that we explicitly define will be shared by all instances of the class.

Static functions

You can use the static keyword to make class methods static. A static method acts on the class itself, not on instances of the class:

class Person{

  constructor(name, age) {

    this.name = name

    this.age = age

  }



  static describe(){

    console.log("This is a person.")

  }



  sayName() {

    console.log("My name is " + this.name)

  }

}



Person.describe()

//logs 'This is a person.'

Notice how static methods operate on the class itself and not an instance of the class. We didn’t have to create a new Person to call the static method.

Static methods are useful for common or shared class functionality. In this case, the describe() method is used to describe what the Person class is. It will apply to every instance of Person. This is why we make it a static method.

Class Inheritance

Inheritance allows you to create new classes based off existing ones. These new classes “inherit” the methods and properties of their parent. They can also override or extend the parent:

class Person{

  constructor(name, age) {

    this.name = name

    this.age = age

  }


  static describe(){

    console.log("This is a person.")

  }


  sayName() {

    console.log("My name is " + this.name)

  }

}



class Programmer extends Person {

  sayName(){

    console.log("My name is " + this.name + " and I am a programmer!")

  }

}



let averageJoe = new Person('Todd', 40)

let programmer = new Programmer('Sam', 33)



averageJoe.sayName()


//logs 'My name is Todd'


programmer.sayName()


//logs 'My name is Sam and I am a programmer!'

Using the extends keyword, we can create a new class sharing the same characteristics as Person. Notice how we override the sayName() method with a new definition for the Programmer class. Apart from overriding this method, everything else remains the same for both Person and Programmer.

Using super

The super keyword allows a child class to invoke parent class properties and methods.

class Person{

  constructor(name, age) {

    this.name = name

    this.age = age

  }



  static describe(){

    console.log("This is a person.")

  }



  sayName() {

    console.log("My name is " + this.name)

  }

}





class Programmer extends Person {

  sayName(){

    super.sayName()

    console.log("My name is " + this.name + " and I am a programmer!")

  }

}



let averageJoe = new Person('Todd', 40)

let programmer = new Programmer('Sam', 33)



programmer.sayName()



//logs 'My name is Sam'

//logs 'My name is Sam and I am a programmer!'

Notice how we call super.sayName() in the Programmer implementation of sayName(). While this invokes the parent implementation of super.sayName(), the name property still references the Programmer class.

Conclusion

Classes facilitate object oriented programming in JavaScript. While regular objects provide similar functionality, classes provide the extra advantage of inheritance and static methods.

Linked Lists in JavaScript With ES6

Linked Lists in JavaScript With ES6

Linked Lists in JavaScript with ES6.This series is about data structure implementation in JavaScript using the ES6 specification.. Let's see how to make a singly linked list in Javascript… ... list in Javascript. We'll be using ES6 syntax throughout.

This is a continuation of a previous piece where we digested all surrounding concepts, pros and cons, Big O time complexity, real use cases, linked-list mainly operations, and all that kind of theory. If you have not read it yet, I recommend you read it first.

This series is about data structure implementation in JavaScript using the ES6 specification.

The aim of this second piece is to walk through the implementation of a linked list. Actually, the two pieces enclose a linked list itself since the prior piece is pointing to this one.

The Node Class

In the next code, we’re going to define our Node class with its constructor. Remember, the node is the basic building block to store the data and the next pointer.

This class will have to handle the node creation. Every time the class is instantiated, the constructor has the responsibility to initialize the two properties: data and next.


Node Class

Now the challenge is to create the next four nodes (just nodes creation, not how to connect them).


Linked List

Basically, we have to instantiate the Node class four times in order to create the four nodes.


Creating Nodes

At this point, we don’t care about the second parameter. Why? Because at this moment, we’re just learning how to create the node without having to worry about how they’ll be connecting together.

How Can We Connect the Nodes?

In the prior code, we just created nodes independently. Now is time to learn how to connect them to form the linked list.


Connecting nodes

We have defined the Node class. Next is to define a new class that will handle the nextpointer property and the main operations in the linked list. Let’s create the LinkedList class.


Linked List Class

In the above code, we have just defined a class called LinkedList with its constructor. This has the work of initializing the headproperty, to store the first node,and size, to keep track of the size of the linked list.

Next is to offer the ability to insert to the head, to the tail, or at any random position in the list.

Inserting Into the Head


Inserting to head

We have just created a simple method to add nodes to the head of the linked list. We are passing down to it the dataparameter and setting a value for the this.head property creating a new instance of the Node class.

Let’s do some tests of its implementation so far and see the results.

The output will be:


Linked list output

Inserting at the Tail

We just learned how to add nodes to the head. It’s time to know how to add nodes to the tail.


Inserting at the tail

In the aboveinsertToTail function, we are passing down the data parameter, and then we created a new instance of the Node class. After that, we are checking if the head is empty. If so, the head itself will be set to the new node we have just after created. Otherwise, set the tail with the head and then loop through the linked list to find the tail and update the tail’s next pointer.

Inserting at Random Position

Finally, we are going to see how to insert a new node in the linked list at a given random position. For this, we have to traverse the list until we find the desired position.

Inserting at a given random position

Now we are going to test this function using the next tests.

The output will be as below. As you can see, at the given index, the node (600) was added at the second index of the list.

The Whole Code

class Node {
  constructor(data, next = null) {
    this.data = data;
    this.next = next;
  }
}

//Let's create four nodes
let node1 = new Node(5);
let node2 = new Node(10);
let node3 = new Node(20);
let node4 = new Node(1);

//connecting nodes
node1.next = node2;
node2.next = node3;
node3.next = node4;

//LinkedList Class
class LinkedList {
  constructor() {
    this.head = null; //first node of the Linked List
    this.size = 0; //Track size of the linked list
  }
  //Insert to head
  insertToHead(data) {
    this.head = new Node(data, this.head);
    this.size++;
  }

  //Insert into the tail
  insertToTail(data) {
    const node = new Node(data);
    let tail = null;
    //if empty, make it head
    if (!this.head) {
      this.head = node;
    } else {
      tail = this.head;
      while (tail.next) {
        tail = tail.next;
      }
      tail.next = node;
    }
    this.size++;
  }

  //Insert at random position
  insertAt(data, index) {
    //if it's empty
    if (!this.head) {
      this.head = new Node(data);
      return;
    }
    //if it needs add to the front of the list
    if (index === 0) {
      this.insertToHead(data); //reuse insertToHead function
      return;
    }
    let node = new Node(data);
    let current, previous;
    let count = 0;
    // current will be first
    current = this.head;
    while (count < index) {
      previous = current;
      count++;
      current = current.next;
    }
    node.next = current;
    previous.next = node;
    this.size++;
  }
}

const linkedList = new LinkedList();
linkedList.insertToHead(100);
linkedList.insertToHead(200);
linkedList.insertToHead(300);
linkedList.insertToTail(400);
linkedList.insertAt(600, 2);

console.table(linkedList);

LinkedList.js

I hope you have gained more knowledge about data structure and especially with Linked list. That’s all for now.

Thanks for reading!

An Introduction to JavaScript ES6 Proxies

An Introduction to JavaScript ES6 Proxies

Proxy is one of the most overlooked concepts introduced in ES6 version of JavaScript, but ES6 proxies bound to come in handy at some point in your future.

Proxy is one of the most overlooked concepts introduced in the ES6 version of JavaScript.

Admittedly, it isn’t particularly useful on a day-to-day basis, but it is bound to come in handy at some point in your future.

The basics

The Proxy object is used to define a custom behavior for fundamental operations such as property lookup, assignment, and function invocation.

The most basic example of a proxy would be:

const obj = {
 a: 1,
 b: 2,
};

const proxiedObj = new Proxy(obj, {
 get: (target, propertyName) => {
   // get the value from the "original" object
   const value = target[propertyName];

   if (!value && value !== 0) {
     console.warn('Trying to get non-existing property!');

     return 0;
   }

   // return the incremented value
   return value + 1;
 },
 set: (target, key, value) => {
   // decrement each value before saving
   target[key] = value - 1;

   // return true to indicate successful operation
   return true;
 },
});

proxiedObj.a = 5;

console.log(proxiedObj.a); // -> incremented obj.a (5)
console.log(obj.a); // -> 4

console.log(proxiedObj.c); // -> 0, logs the warning (the c property doesn't exist)

We have intercepted the default behavior of both get and set operations by defining the handlers with their respective names in the object provided to the proxy constructor. Now each get operation will return the incremented value of the property, while set will decrement the value before saving it in the target object.

What’s important to remember with proxies is that once a proxy is created, it should be the only way to interact with the object.

Different kinds of traps

There are many traps (handlers that intercept the object’s default behavior) aside from get and set, but we won’t be using any of them in this article. With that being said, if you are interested in reading more about them, here’s the documentation.

Having fun

Now that we know how proxies work, let’s have some fun with them.

Observing object’s state

As it has been stated before it is very easy to intercept operations with proxies. To observe an object’s state is to be notified every time there’s an assignment operation.

const observe = (object, callback) => {
 return new Proxy(object, {
   set(target, propKey, value) {
     const oldValue = target[propKey];
   
     target[propKey] = value;

     callback({
       property: propKey,
       newValue: value,
       oldValue,
     });

     return true;
   }
 });
};

const a = observe({ b: 1 }, arg => {
 console.log(arg);
});

a.b = 5; // -> logs from the provided callback: {property: "b", oldValue: 1, newValue: 5}

And that’s all we have to do — invoke the provided callback every time the set handler is fired.

As an argument to the callback, we provide an object with three properties: the name of the changed property, the old value, and the new value.

Prior to executing the callback, we assign the new value in the target object so the assignment actually takes place. We have to return true to indicate that the operation has been successful; otherwise, it would throw a TypeError.

Here’s a live example.

Validating properties on set

If you think about it, proxies are a good place to implement validation — they are not tightly coupled with the data itself. Let’s implement a simple validation proxy.

As in the previous example, we have to intercept the set operation. We would like to end up with the following way of declaring data validation:

const personWithValidation = withValidation(person, {
 firstName: [validators.string.isString(), validators.string.longerThan(3)],
 lastName: [validators.string.isString(), validators.string.longerThan(7)],
 age: [validators.number.isNumber(), validators.number.greaterThan(0)]
});

In order to achieve this, we define the withValidation function like so:

const withValidation = (object, schema) => {
 return new Proxy(object, {
   set: (target, key, value) => {
     const validators = schema[key];

     if (!validators || !validators.length) {
       target[key] = value;

       return true;
     }

     const shouldSet = validators.every(validator => validator(value));

     if (!shouldSet) {
       // or get some custom error
       return false;
     }

     target[key] = value;
     return true;
   }
 });
};

First we check whether or not there are validators in the provided schema for the property that is currently being assigned — if there aren’t, there is nothing to validate and we simply assign the value.

If there are indeed validators defined for the property, we assert that all of them return true before assigning. Should one of the validators return false, the whole set operation returns false, causing the proxy to throw an error.

The last thing to do is to create the validators object.

const validators = {
 number: {
   greaterThan: expectedValue => {
     return value => {
       return value > expectedValue;
     };
   },
   isNumber: () => {
     return value => {
       return Number(value) === value;
     };
   }
 },
 string: {
   longerThan: expectedLength => {
     return value => {
       return value.length > expectedLength;
     };
   },
   isString: () => {
     return value => {
       return String(value) === value;
     };
   }
 }
};

The validators object contains validation functions grouped by the type they should validate. Each validator on invocation takes the necessary arguments, like validators.number.greaterThan(0), and returns a function. The validation happens in the returned function.

We could extend the validation with all kinds of amazing features, such as virtual fields or throwing errors from inside the validator to indicate what went wrong, but that would make the code less readable and is outside the scope of this article.

Here’s a live example.

Making code lazy

For the final — and hopefully most interesting — example, let’s create a proxy that makes all the operations lazy.

Here’s a very simple class called Calculator, which contains a few basic arithmetic operations.

class Calculator {
 add(a, b) {
   return a + b;
 }

 subtract(a, b) {
   return a - b;
 }

 multiply(a, b) {
   return a * b;
 }

 divide(a, b) {
   return a / b;
 }
}

Now normally, if we ran the following line:

new Calculator().add(1, 5) // -> 6

The result would be 6.

The code is executed on the spot. What we would like is to have the code wait for the signal to be run, like a run method. This way the operation will be postponed until it is needed — or not executed at all if there is never a need.

So the following code, instead of 6, would return the instance of the Calculator class itself:

lazyCalculator.add(1, 5) // -> Calculator {}

Which would give us another nice feature: method chaining.

lazyCalculator.add(1, 5).divide(10, 10).run() // -> 1

The problem with that approach is that in divide, we have no clue of what the result of add is, which makes it kind of useless. Since we control the arguments, we can easily provide a way to make the result available through a previously defined variable — $, for example.

lazyCalculator.add(5, 10).subtract($, 5).multiply($, 10).run(); // -> 100

$ here is just a constant Symbol. During execution, we dynamically replace it with the result returned from the previous method.

const $ = Symbol('RESULT_ARGUMENT');

Now that we have a fair understanding of what do we want to implement, let’s get right to it.

Let’s create a function called lazify. The function creates a proxy that intercepts the get operation.

function lazify(instance) {
 const operations = [];

 const proxy = new Proxy(instance, {
   get(target, propKey) {
     const propertyOrMethod = target[propKey];

     if (!propertyOrMethod) {
       throw new Error('No property found.');
     }

     // is not a function
     if (typeof propertyOrMethod !== 'function') {
       return target[propKey];
     }

     return (...args) => {
       operations.push(internalResult => {
         return propertyOrMethod.apply(
           target,
           [...args].map(arg => (arg === $ ? internalResult : arg))
         );
       });

       return proxy;
     };
   }
 });

 return proxy;
}

Inside the get trap, we check whether or not the requested property exists; if it doesn’t, we throw an error. If the property is not a function, we return it without doing anything.

Proxies don’t have a way of intercepting method calls. Instead, they are treating them as two operations: the get operation and a function invocation. Our get handler has to act accordingly.

Now that we are sure the property is a function, we return our own function, which acts as a wrapper. When the wrapper function is executed, it adds yet another new function to the operations array. The wrapper function has to return the proxy to make it possible to chain methods.

Inside the function provided to the operations array, we execute the method with the arguments provided to the wrapper. The function is going to be called with the result argument, allowing us to replace all the $ with the result returned from the previous method.

This way we delay the execution until requested.

Now that we have built the underlying mechanism to store the operations, we need to add a way to run the functions — the .run() method.

This is fairly easy to do. All we have to do is check whether the requested property name equals run. If it does, we return a wrapper function (since run acts as a method). Inside the wrapper, we execute all the functions from the operations array.

The final code looks like this:

const executeOperations = (operations, args) => {
 return operations.reduce((args, method) => {
   return [method(...args)];
 }, args);
};

const $ = Symbol('RESULT_ARGUMENT');

function lazify(instance) {
 const operations = [];

 const proxy = new Proxy(instance, {
   get(target, propKey) {
     const propertyOrMethod = target[propKey];

     if (propKey === 'run') {
       return (...args) => {
         return executeOperations(operations, args)[0];
       };
     }

     if (!propertyOrMethod) {
       throw new Error('No property found.');
     }

     // is not a function
     if (typeof propertyOrMethod !== 'function') {
       return target[propKey];
     }

     return (...args) => {
       operations.push(internalResult => {
         return propertyOrMethod.apply(
           target,
           [...args].map(arg => (arg === $ ? internalResult : arg))
         );
       });

       return proxy;
     };
   }
 });

 return proxy;
}

The executeOperations function takes an array of functions and executes them one by one, passing the result of the previous one to the invocation of the next one.

And now for the final example:

const lazyCalculator = lazify(new Calculator());

const a = lazyCalculator
 .add(5, 10)
 .subtract($, 5)
 .multiply($, 10);

console.log(a.run()); // -> 100

If you are interested in adding more functionality I have added a few more features to the lazify function — asynchronous execution, custom method names, and a possibility to add custom functions through the .chain() method. Both versions of the lazify function are available in the live example.

Summary

Now that you have seen proxies in action, I hope that you could find a good use for them in your own codebase.

Proxies have many more interesting uses than those covered here, such as implementing negative indices and catching all the nonexistent properties in an object. Be careful, though: proxies are a bad choice when performance is an important factor.