DEV Community

Cover image for Understanding the four Pillars of Object-Oriented Programming using JavaScript 💊
Adhiraj Kinlekar
Adhiraj Kinlekar

Posted on • Updated on

Understanding the four Pillars of Object-Oriented Programming using JavaScript 💊

Object Oriented Programming

Object-Oriented Programming is a programming paradigm that enables you to model and structure your code using objects and classes. JavaScript is not a full-fledged OOP language, but you can still leverage OOP's core principles to write cleaner and more maintainable code. There are four main pillars of Object-Oriented Programming:

Abstraction

Abstraction means hiding the complex implementation details and exposing only what is necessary. Even though JavaScript lacks interfaces or abstract classes, we can still achieve abstraction through other means.

One effective approach for implementing abstraction is to expose only the necessary methods and then, through this exposed method, invoke the private methods of the class. This strategy effectively conceals the underlying complexities, which is a fundamental aspect of abstraction.

class Auth {

    constructor() {}

    #validate() {
    // Private method
    }

    #sendOnboardingEmail() {
    // Private method
    }

    // Public method that calls private methods
    signUp() {
      this.#validate();
      // Execute signUp functionality
      this.#sendOnboardingEmail();
    }
}

const auth = new Auth();

// Calls the public method, which, in turn, calls private methods
auth.signUp(); 
Enter fullscreen mode Exit fullscreen mode

Encapsulation

When you search the internet for information about Abstraction and Encapsulation, you will likely come across numerous articles that sometimes present conflicting ideas. In my understanding, although abstraction and encapsulation are different concepts, they often complement each other. In the code block above, private accessors are utilized, enabling controlled access to the class, aligning with the principles of Encapsulation. Encapsulation promotes the idea of bundling data and the functions that operate on that data into a single, self-contained package. This encapsulated entity can control how the data is accessed, modified, or interacted with.

Even though Encapsulation is an OOP concept, it can be implemented without the use of classes and objects by utilizing Closures. A closure is a mechanism that enables an inner function to access the variables and parameters of its outer function, even after the outer function has completed its execution. Closures achieve encapsulation by packaging the actual code (the body of the function) along with the variables and parameters that the function can access during its execution. The exclusive method for accessing the encapsulated data is through the function.

function createCounter() {

  let count = 0; // This variable is encapsulated within the closure.

  function increment() {
    count++; // The inner function can access the 'count' variable.
    console.log(count)
  }

  return increment; // Return the inner function (closure).
}

const counter = createCounter(); 
Enter fullscreen mode Exit fullscreen mode

Inheritance

When a class acquires the members and behaviors of its parent class, it is known as Inheritance. Inheritance provides code reusability and encourages modular design by breaking down a complex system into smaller, manageable components. When you need to make changes or updates to shared functionality, you can do so in the base class. These changes automatically apply to all derived classes, reducing maintenance efforts and ensuring consistency throughout your codebase.

class Person {

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

  sayHello() {
    console.log(`Hello, my name is ${this.name}, and I am ${this.age} years old.`);
  }
}

// Create a derived class Student that inherits from Person
class Student extends Person {

  constructor(name, age, studentId) {
    super(name, age); // Call the parent class constructor using super
    this.studentId = studentId;
  }

  study(subject) {
    console.log(`${this.name} with student ID ${this.studentId} is studying ${subject}.`);
  }
}

// Create instances of Person and Student
const person1 = new Person('Alice', 30);

const student1 = new Student('Bob', 25, '12345');

// Use the inherited methods
person1.sayHello();

student1.sayHello();

student1.study('Math');
Enter fullscreen mode Exit fullscreen mode

Polymorphism

The term 'polymorphism' means having many forms. The concept of polymorphism enables us to perform different operations in various scenarios. In object-oriented programming languages such as C#, polymorphism is achieved through the use of interfaces and abstract classes, as well as through the use of virtual methods and overriding in inheritance. While JavaScript does not provide comprehensive support for polymorphism, we can still achieve it.

Polymorphism can be attained by using inheritance and by overriding the base class. You do not need to explicitly indicate that a method is being overridden since JavaScript uses a prototype-based inheritance model, and method overriding is accomplished simply by defining a method with the same name in a subclass. The new method in the subclass effectively replaces the method with the same name in the base class, allowing you to perform different actions in different scenarios, which aligns with the concept of polymorphism

class Transport {

  constructor() {}

  drive() {
    console.log("Driving...");
  }

  fly() {
    console.log("Flying...");
  }
}

// Create a derived class Car that inherits from Transport
class Car extends Transport {

  constructor() {
    super();
  }

// Overrides it's base implementation
  fly() {
    console.log("I am a car, I cannot fly");
  }
}

const audi = new Car();

audi.fly();
Enter fullscreen mode Exit fullscreen mode

Song of the day : Agalloch - Hawthorne Passage

Top comments (13)

Collapse
 
lnahrf profile image
Lev N.

From my personal experience in corporations and startups alike, using Javascript OOP on the frontend usually creates an unintentional mess of polymorphism. OOP is great, but it has it's uses, just like functional programming.

Collapse
 
efpage profile image
Eckehard • Edited

My understanding is, that you web development can greatly benefit from using OO principles. But currently I do not see much support for this idea.

Collapse
 
adhirajk profile image
Adhiraj Kinlekar

Thank you for sharing your thoughts! I appreciate your perspective. I kind of agree with you that there is not much support for OOP on the frontend in the web dev world. However, there's always Angular and the older version of React, which implement OOP principles very well. Even on the backend, I have been able to write clean, modular code using Node.js.

Collapse
 
efpage profile image
Eckehard

Maybe you like to try out DML. I wrote this library to build fully reactive frontends.

DML does not force to use OOP, but it creates some prerequisites to write OOP code:

  • DOM elements or composites are generated using functions.You do not need any HTML, so it does not force you to use any global variables.
  • You can store DOM references in local variables, which is a precondition for ENCAPSULATION.
  • If you generate DOM elements or groups of DOM elements using classes, all DOM references can be held inside the class objects. So, all the mechanics (functions and events related to the objects) can be encapsulated by the class.

DOM elements are already objects, so there is not much to do for for the standard HTML-elements. But with DML, you can extend this concept to self generated objects, that are generated the same way as standard elements. Think of an image carousel, a complex input element or any UI-element, that contains more than one DOM element. On the DML homepage (which was generated using DML), there is this funny jell yfish-menue. This can be generated using one single line of code, all interactions fully encapsulated the a class.

The DML homepage is pretty fast, though the code is not much optimizes (no bundler used). Please do your own performance checks to see, if this could be an option for your projects. I´m just about to release a new version, but the core will be very similar, so it´s worth a try.

Thread Thread
 
adhirajk profile image
Adhiraj Kinlekar

Thank you for the in-depth explanation. Your initial comment is much clearer to me now. I will definitely check out this library.

Collapse
 
misscoder profile image
Helina Coder

do you think oop is enough?

Collapse
 
adhirajk profile image
Adhiraj Kinlekar

Hey Helina, thank you for your comment! Could you please elaborate on your question? Are you referring to its suitability for specific tasks, or do you have a particular concern about its limitations?

Collapse
 
misscoder profile image
Helina Coder

what im mean is that there are other programming paradigms, but the oop is the most famous so my question is, do i have to learn other paradigms besides oop or is it enough

Thread Thread
 
adhirajk profile image
Adhiraj Kinlekar • Edited

In my opinion, once you are comfortable with OOP, you can start looking into functional programming, but it is not a necessity; however, it is always good to know. I have read that many features in the React ecosystem, such as state management in Redux, leverage functional programming principles.

Thread Thread
 
peerreynders profile image
peerreynders • Edited

If you look at the 2004 paper The Structure and Interpretation of the Computer Science Curriculum the educators involved found that students typically had an easier time starting with functional programming and then transitioning to class-based object-oriented programming.

Given my personal experience I have no trouble accepting that, given the amount of unlearning I had to do when moving from OO to FP (and beyond). This group of researchers eventually had to concede that a scheme-like language isn't ideal for all students which is why they created Pyret. And in their most recent efforts they don't focus on either FP or OO but on data: A Data-Centric Introduction to Computing (without falling back into structured programming; they also elaborate where Python falls short for their purposes).

James Coplien jokes that JavaScript is the only object-oriented language because the code actually manipulates objects directly while all the mainstream class-oriented languages only create objects via classes. In class-oriented languages an instance has a lifetime membership with its class and in most cases that membership significantly impacts its behaviour.

In JavaScript an object tracks its constructor but other than that a class is just a template for creating objects. After creation an individual object can be augmented in any number of ways (Monkey Patching) blurring its relationship to the creating class.

From that view the term object is unfortunate. “Tables are the main (in fact, the only) data structuring mechanism in Lua, and a powerful one.”.

Once plain JavaScript objects are simply viewed as key-value data containers and “functions are objects” is accepted as a convenience feature the Function-Oriented nature of JS emerges, especially when one considers its Scheme-y past.

Granted JavaScript is said to support object-oriented, imperative, and declarative (e.g. functional programming) styles but there are trade-offs to be considered.

The most amazing achievement of the computer software industry is its continuing cancellation of the steady and staggering gains made by the computer hardware industry.

— Henry Petroski

The notion of a performance budget is something you would typically expect with embedded products, not necessarily something application-level developers would have to concern themselves with. Given the engineering behind products like TurboFan JavaScript is already faster than it has any right to be but it can't compare to native (though Static Hermes may change that).

Both JavaScript and WebAssembly have the same peak performance. They are equally fast. But it is much easier to stay on the fast path with WebAssembly than it is with JavaScript. Or the other way around. It is way too easy sometimes to unknowingly and unintentionally end up in a slow path in your JavaScript engine than it is in the WebAssembly engine.

WebAssembly for Web Developers (Google I/O ’19)

Classes also interfere with tree-shaking. For this reason libraries like RxJS which started out as class-heavy ports of the C#/Java originals, now minimize their use of classes and limit the methods on those that remain to the absolute, bare minimum.

Keep in mind that today's mobile devices vary wildly in their performance characteristics and capabilities; so while the flagship devices keep improving, the average, commodity devices really aren't.

When faced with a similar reality with regards to gaming consoles in 2009 the gaming industry started moving from OO to Data-Oriented Design and embracing the Entities, Components, Systems (ECS) approach.

Object-oriented development is good at providing a human oriented representation of the problem in the source code, but bad at providing a machine representation of the solution. It is bad at providing a framework for creating an optimal solution, so the question remains: why are game developers still using object-oriented techniques to develop games? It's possible it's not about better design, but instead, making it easier to change the code. It's common knowledge that game developers are constantly changing code to match the natural evolution of the design of the game, right up until launch. Does object-oriented development provide a good way of making maintenance and modification simpler or safer?

Data-Oriented Design: Mapping the problem

Note that often the contention that imperative or OO programming is “easier” is a result of the familiarity heuristic; subjectively we perceive whatever we already know (e.g. learned first) as “easier”—just because something is different doesn't mean it's difficult.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.