Object oriented programming in javascript

Construction functions

Befor class keyword was introduced in ES6, below syntax was being used to achieve the inheritance. class keyword is a syntactic sugar.

//constructor function
function F1(id,name){
    this.id=id;
    this.name = name;
}

//m1 is prototype method meaning all objects created by new F1() will inherit it
F1.prototype.m1 = function(){
    console.log(this.name + " name ")
}

let o1 = new F1(1,"sabgar")
//we are able to call m1 due to prototypical inheritance
o1.m1()

o1 instanceof F1

class and constructor keyword (es6) - Syntactic sugar for Constructor Functions

As you can see, we can create classes in JS using "class" and "constructor" keywords. But this is just a syntactic sugar for function F1.

//class

//F1 is actually transformed into a function by interpreter/compiler
class F1{
    constructor(id,name){
        this.id = id
        this.name = name
    }

    /*   
     - add # to the variable to make it private
     - private method still in progress
    */

    //m1 is added to prototype of function F1.
    m1() {
         console.log(this.name + " name ")
   
    }
}

let o1 = new F1(1,"sss")
o1.m1()

Extending classes (Inheritance) and polymorphism in JS

  • inherited objects not copied unlike Java
  • Inheritance is achieved through prototypes
  • Only functions have prototype e.g. Array is a function that has a prototype where all array methods are stored.

class Car {
  constructor(make,model){
      this.make = make
      this.model = model
  }

  getCarMake(){
    return this.make  
  }
}

class ElectricCar extends Car{
  
  //add extra props here
  constructor(make,model,batteryCapacity){
      super(make,model)
      this.batteryCapacity = batteryCapacity
  }

  //method overriding
  getCarMake(){
      return "Electric car by " + this.make 
  }

  //method overloading
  getCarMake(x){
      return "Electric car by " + this.make + this.model
  }
}

let teslaModelY = new ElectricCar("Tesla","Model Y", "2000")
console.log(teslaModelY.__proto__)
Car.prototype.isPrototypeOf(teslaModelY) //returns true
//teslaModelY.__proto__.__proto__ -> here is inherited method (Car prototype method)
//just follow chain to find the method

this binding


  //binding using new keyword
  function F1(id,name){
      this.id=id;
      this.name = name;
  }
  //implicit binding
  let person = {
      name : "sdsd",
      hi(){
          console.log(this.name)
      }
  }
  //explicit binding
  let person = {
      name : "sdsd",
      hi: function (){
          console.log(this.setTimeOut)
      }.bind(window)
  }
  //Arrow functions - lexical binding
  let person = {
      name : "sdsd",
        hi(){
          let inner = () => {
              //this refers to person due to lexical scope binding
              console.log(this.name)
          }
          inner()
        }
  }
  /*
  this refers the object that invoked the function
  in global context, it is window/global object
  in arrow functions, this refers to the global object
  same function can be called by different object and this can be used to
    change the behaviour of function based on object.
  arrow functions are lexically bound to surrounding object
  In named functions, this refers to the global object unless function is called 
  by an object
  this keyword is dynamically scoped. it does not matter if it is wrapped in a
    object. what matters is how/which object called the function
  */
  let o1 = {
      f1(){
  
          //this refers to object o1
          console.log("f1", this)
  
          function f2(){
          //this refers to window object as f2 was not called by o1
          console.log("f2", this)
          }
          f2()
      }
  }
  
  o1.f1();
  let o1 = {
      f1(){
  
          //this refers to object o1
          console.log("f1", this)
  
          let f2 = () => {
          //this refers to o1 as f2 is arrow function and f2 is called by the 
          // function which was in turn called by o1. So it looks for nearest
          //object that called the function using scope chain
  
          console.log("f2", this)
          }
          f2()
      }
  }
  
  o1.f1();
  //above can be also achieved by using bind - old way
  let o1 = {
      f1(){
  
          //this refers to object o1
          console.log("f1", this)
  
          let f2 = function() {
          //this refers to window object as f2 was not called by o1
          console.log("f2", this)
          }
          //bind o1 to f2. if you do not bind, this will refer to window object
          // inside f2 function
          f2.bind(this)()
      }
  }
  
  o1.f1();

Web development and Automation testing

solutions delivered!!