Is JavaScript Object Oriented Programming?

Mateen Kiani

Mateen Kiani

Published on Tue Jun 24 2025·6 min read

is-javascript-object-oriented-programming?

JavaScript has changed a lot since its early days, but one thing has stayed true: it still relies on objects. We often talk about functions, callbacks, and promises, yet we skip over how deep the object system really goes. Can JavaScript offer the same object oriented structure we see in classic languages like Java or C#?

It can, once you see how prototype chains, constructor functions, and ES6 classes fit together. By learning how these parts interact, you can write cleaner code, prevent hidden bugs, and choose the right approach for your project. The next sections will guide you from basic objects to advanced design patterns, so you feel confident building any feature with JavaScript.

Basics of JavaScript Objects

At its core, JavaScript is built around objects. An object is a collection of key-value pairs where keys are strings or symbols. You can create an object in several ways:

  • Using an object literal, for example:
const user = { name: 'Alice', age: 30 }
  • Using Object.create, which sets up the prototype:
const base = { greet() { return 'hi' } }
const person = Object.create(base)
person.name = 'Bob'

Tip: You can convert an object to a JSON string or parse JSON into an object. See more on JSON.

Objects hold data and behavior. Each object has an internal link to a prototype. This link, called [[Prototype]], determines what properties and methods are available if you don't define them directly on the object.

Understanding this link is key before you move on to classes. It also explains why adding or changing a method on one object can affect others created from the same prototype.

Prototypes and Inheritance

JavaScript uses prototype based inheritance instead of classes at a low level. When you access a property, the engine looks on the object itself. If it does not find it, it follows the prototype chain. This chain can go on until it hits Object.prototype or null.

Here is how prototype inheritance works:

function Animal(type) {
this.type = type
}
Animal.prototype.speak = function() {
return `I am a ${this.type}`
}
const dog = new Animal('dog')
console.log(dog.speak()) // I am a dog

In this example, speak is not on dog, but on Animal.prototype. The new keyword links dog to that prototype. This setup defines shared behavior without copying functions to each instance.

Tip: Use Object.getPrototypeOf(obj) to inspect an object’s prototype at runtime. It helps debug inheritance chains.

Prototype chains are flexible. You can swap prototypes, extend built ins, or even create custom chains. But with great power comes great responsibility. If you overuse prototypes, your code can become hard to follow.

ES6 Classes Overview

With ES6, JavaScript introduced a class syntax that looks more familiar to developers from classical OOP languages. Under the hood, it still uses prototypes, but the code reads cleaner:

class User {
constructor(name) {
this.name = name
}
greet() {
return `Hello, ${this.name}`
}
}
const u = new User('Eve')
console.log(u.greet()) // Hello, Eve

Classes let you define constructors and methods in one place. You can also use extends and super for inheritance:

class Admin extends User {
constructor(name, role) {
super(name)
this.role = role
}
getRole() {
return this.role
}
}

Tip: Even with classes, remember prototypes drive the actual inheritance. You can still use Object.setPrototypeOf if you need to tweak links.

ES6 classes group related logic and make patterns like factory or singleton easier to read. They also work nicely with tools like Babel and TypeScript.

Encapsulation and Privacy

One common OOP idea is to hide internal details. JavaScript did not offer private properties until recently. Now you can use private fields with the # syntax:

class Counter {
#count = 0
increment() {
this.#count++
}
getCount() {
return this.#count
}
}
const c = new Counter()
c.increment()
console.log(c.getCount()) // 1

Before private fields, developers used closures and modules to hide data:

function createBankAccount() {
let balance = 0
return {
deposit(amount) {
balance += amount
},
getBalance() {
return balance
}
}
}

Tip: Use modules or closures for older environments that do not support private fields.

Encapsulation prevents outside code from changing internal state in unexpected ways. It leads to more robust APIs and safer libraries.

Design Patterns in JS

Design patterns give you a template for common problems. In JavaScript, you can adapt patterns from other languages or invent new ones. Here are a few:

  • Factory Pattern: Create objects without exposing new.
  • Singleton Pattern: Ensure only one instance of a class exists.
  • Module Pattern: Encapsulate related functions and state.
  • Observer Pattern: Let objects subscribe to events and get notified.
// Module pattern example
const Logger = (function() {
let logs = []
function log(message) {
logs.push({ message, timestamp: Date.now() })
}
function getLogs() {
return logs
}
return { log, getLogs }
})()
Logger.log('start')
console.log(Logger.getLogs())

These patterns make code predictable and easier to maintain. They also let teams share a common vocabulary.

Tip: Choose a pattern that fits your project size and team. Avoid overengineering small scripts.

OOP in Modern Frameworks

Many frameworks let you use OOP principles. For example, in React you can write class components:

import React, { Component } from 'react'
class App extends Component {
render() {
return <div>Welcome {this.props.name}</div>
}
}

But hooks and functions are now more common. Even then, you can group logic into custom hooks to mimic encapsulation:

import { createContext, useContext, useState } from 'react'
const AuthContext = createContext()
export function AuthProvider({ children }) {
const [user, setUser] = useState(null)
return (
<AuthContext.Provider value={{ user, setUser }}>
{children}
</AuthContext.Provider>
)
}
export function useAuth() {
return useContext(AuthContext)
}

Tip: See more on React Context for a deep dive on context.

Frameworks like Angular or Vue use classes, decorators, or reactive data. OOP ideas help organize components, services, and modules into coherent systems.

Conclusion

JavaScript is truly object oriented at its heart. With prototypes as the base, ES6 classes offer a friendly syntax, and private fields or closures bring encapsulation. Design patterns help you structure code and frameworks let you apply OOP to real apps. When you understand how each piece works, you can avoid traps like unexpected prototype changes or leaky data. Start with object literals, learn the prototype chain, and then explore classes. Try encapsulating logic in modules or hooks. With these tools in hand, you can build robust, maintainable code. Embrace JavaScript OOP and see your projects become clearer and more scalable.


Mateen Kiani
Mateen Kiani
kiani.mateen012@gmail.com
I am a passionate Full stack developer with around 3 years of experience in MERN stack development and 1 year experience in blockchain application development. I have completed several projects in MERN stack, Nextjs and blockchain, including some NFT marketplaces. I have vast experience in Node js, Express, React and Redux.