DEV Community

Mohammed Ali
Mohammed Ali

Posted on • Updated on

OOP in JS 1/2

Paradigm refers to style of code, how its organized. Common Programming paradigms are: OOP, Functional etc. To be a Dev, you need to know OOP well.

OOP

  • most popular enterprise programming paradigm.
  • based on objects
  • developed with the goal of organizing code
  • makes code more flexible, maintainable.
  • Before OOP, code was scattered across multiple fns in global scope without any structure. That style was called spaghetti code which is very hard to maintain, forget about adding new functionality.
  • used to create objects via code.
  • also enables interaction of objects with each other.

API

  • methods that the code outside of the object can access and use to communicate with other objects.

Class

  • abstract blueprint to create objects.
  • instantiated from classes i.e created using the class blueprint.
  • multiple objects are created from a class using 'new Class()' syntax

Design of class:

  • Done using 4 priciples of OOP namely Abstraction, Encapsulation, Inhertitance, Polymorphism
  • Abstraction: Hiding unnecessary details which don't matter to the end user.
  • Encapsulation: Keeping some properties-methods as private which makes them accessible only from inside the class, and making them inaccessible from outside the class. Expose few methods as public interface API for interacting with the external world. Hence, it prevents external code from manipulating the internal state[object's data] as this can be a great source of bugs. Public interface is the code which is not private. Making methods private makes it easier for us to change the implementation of the code without breaking the external dependencies. Summary: Nicely encapsulate state and methods and only make essential methods public.
  • Inhertitance: Duplicate code is hard to maintain. Hence, this concept supports reusability of code by inheriting the already written code. Child class extends the parent class by inheriting all the properties & methods from parent class. Also, child class implements its own data-functionality along with inherited features.
  • Polymorphism: A child class can overwrite an inherited method from parent class.

Objects are:

  • used to model real world or abstract features.
  • may contain data(properties) and code(methods). help us to pack data & code in one block
  • self-contained pieces/blocks of code.
  • building blocks of appln, interact with each other.
  • Interaction b/w objects happen through public interface API.
  • all objects created from a class are called instance of that class.
  • all objects can have different data in them, but they all share common functionality

Classical Inheritance:

  • Supported by Java, C++, Python etc
  • one class inherits from another class
  • methods or behavior is copied from class to all instances.

Delegation or Prototypal Inheritance in JS:

  • Supports all OOP principles like in classical languages.
  • an instance inheriting from a class.
  • Prototype contains all the methods, that are accessbile to all the objects linked to that prototype. prototype: contains methods object: can access methods of prototype, connected to prototype object using proto link.
  • objects inherit properties & method defined on the prototype object.
  • objects delegate behavior to the prototype object.
  • An user-defined array instance accesses the .map() on Array.prototype.map() i.e map() defined on prototype object via proto link. Hence, .map() is not defined on our instance, rather its defined on the prototype.
## 3 Ways to implement Prototypal Inheritance via:
1. Constructor Fn:
- To create objects via function.
- Only difference from normal fn is that they are called with 'new' operator.
- Convention: always start with a capital letter to denote constructor fn. Even builtins like Array, Map also follow this convention.
- An arrow function doesn't work as Fn constructor as an arrow fn doesn
t have its own 'this' keyword which we need with constructor functions.
- Produces an object. 
- Ex. this is how built-in objects like Array, Maps, Sets are implemented

2. ES6 Classes:
- Modern way, as compared to above method.
- Syntactic sugar, although under the hood work the same as above syntax.
- ES6 classes doesn't work like classical OOP classes.

3. Object.create()
- Way to link an object to its prototype
- Used rarely due to additional repetitive work.

Enter fullscreen mode Exit fullscreen mode
## What does 'new' operator automates behind the scene?
1. Create an empty object {} and set 'this' to point to this object.
2. Create a __proto__ property linking the object to its parent's prototype object.
3. Implicit return is added, i.e automatically return 'this {} object' from the constructor fn.

- JS doesn't have classes like classical OOP, but it does create objects from constructor fn. Constructor fn have been used since inception to simulate class like behavior in JS.
Ex. validate if an object is instance of a constructor fn using "instanceOf" operator.

const Person = function(fName, bYear) {
  // Instance properties as they will be available on all instances created using this constructor fn.
  this.fName = fName;
  this.bYear = bYear;

  // BAD PRACTICE: NEVER CREATE A METHOD INSIDE A CONSTRUCTOR FN.
  this.calcAge = function(){
console.log(2024 - this.bYear);
}
};

const mike = new Person('Mike', 1950);
const mona = new Person('Mona', 1960);
const baba = "dog";

mike; // Person { fName: 'Mike', bYear: 1950 }
mona; // Person { fName: 'Mona', bYear: 1960 }

mike instanceof Person; // true
baba instanceof Person; // true


If there are 1000+ objects, each will carry its own copy of fn defn.
Its a bad practice to create a fn inside a contructor fn as it would impact performance of our code.
Enter fullscreen mode Exit fullscreen mode

Prototype object:

  • Each function, inclduding constructor fn in JS has a property called prototype object.
  • Every object created from this constructor fn, will have access to prototype object of the constructor fn. Ex. Person.prototype
  • Add fn to this prototype object via: Person.prototype.calcAge = function(bYear){ console.log(2024 - bYear); };

mike.calcAge(1970); // 54
mona.calcAge(1940); // 84

  • mike object won't contain the .calcAge() but it will access it using the proto link defined on the Person.prototype object.
  • 'this' is always set to the object calling the function.

mike.proto; // { calcAge: [Function (anonymous)] }
mona.proto; // { calcAge: [Function (anonymous)] }

mike.proto === Person.prototype; // true

  • Person.prototype here serves as prototype for all the objects, not just this single object created using Person constructor fn.

Person.prototype.isPrototypeOf(mike); // true
Person.prototype.isPrototypeOf(Person); // false

  • prototype should have been better named as prototypeOfLinkedObjects generated using the Constructor fn.
  • Not just methods, we can create properties also on prototype object. Ex. Person.prototype.creatureType = "Human"; mike.creatureType; // Human mona.creatureType; // Human

Different properties for an object:

  • Own property and properties on constructor fn accessbile via proto link of objects.
  • To check own property for objects, use:
    mike.hasOwnProperty('fName'); // true
    mona.hasOwnProperty('creatureType'); // false

  • Two way linkage:
    Person() - constructor fn
    Person.prototype - Prototype

Person() constructor fn links to Person.prototype via .prototype
Person.prototype prototype links back to Person() constructor fn via .constructor to Person() itself.

proto : always points to Object's prototype for all objects in JS.
newly created object is automatically returned, unless we explicitly return something else and stored in the LHS variable declared.

Prototype Chain:

  • Similar to scope chain. Look for variable in the scope level
  • Prototype chain has to lookup for finding properties or methods.
  • All objects in JS has a proto link Person Person.proto Person.proto.proto; // [Object: null prototype] {}

Top Level Object in JS:
Object() - constructor fn
Object.prototype - Prototype
Object.prototype.proto // null

Object.prototype methods:

  • constructor: f Object()
  • hasOwnProperty
  • isPrototypeOf
  • propertyIsEnumerable
  • toLocaleString
  • toString
  • valueOf
  • defineGetter etc

// Takes to constructor fn prototype
mike.proto === Person.prototype; // true
// Takes to parent of constructor fn's prototype i.e Object fn
mike.proto.proto; // [Object: null prototype] {}
// Takes to parent of Object fn i.e end of prototype chain
mike.proto.proto.proto; // null

  • All fns in JS are objects, hence they also have a prototype
  • console.dir(x => x+1);
  • Fns are objects, and objects have prototypes. So a fn prototype has methods which can be called.
  • const arr = [2,4,21]; // is same as using 'new Array' syntax
    arr.proto; // shows fns on array's prototype

  • Each array doesn't have all of these methods, its able to use it via proto link.

  • arr.proto === Array.prototype; // true

const arr = [2,4,21];
arr.proto; // Array prototype
arr.proto.proto; // Object prototype
arr.proto.proto.proto; // null

## If we add a fn to Array.prototype, then all newly created arrays will inherit that method. However extending the prototype of a built-in object is not a good idea. Incase new version of JS adds a method with the same name, will break your code. Or in case multiple Devs create similar fnality with different names will add an unnecessary overhead.
Ex. Add a method named unique to get unique values
const arr = [4,2,4,1,2,7,4,7,3];
Array.prototype.uniq = function(){
return [...new Set(this)];
}
arr.uniq(); // [ 4, 2, 1, 7, 3 ]
Enter fullscreen mode Exit fullscreen mode
  • All DOM elements behind the scene are objects.
  • console.dir(h1) // will show you it in object form
  • Prototype chain: h1 -> HTMLHeadingElement -> HTMLElement -> Element -> Node -> EventTarget -> Object

Top comments (0)