What is Object-Oriented Programming (OOP)? A complete guide
Object-oriented programming is a paradigm based on objects that combine data and behavior. Learn the four pillars — encapsulation, inheritance, polymorphism, and abstraction — with practical examples and how they work in JavaScript, Java, and Python.
On this page
- Where OOP came from
- Objects, classes, and instances
- Encapsulation and information hiding
- Inheritance
- Polymorphism and dynamic dispatch
- Abstraction and open recursion
- OOP in the real world
- FAQ
- What is the difference between a class and an object?
- Is JavaScript an object-oriented language?
- What are the four pillars of OOP?
- Is composition better than inheritance?
- Summary
Object-oriented programming (OOP) is a programming paradigm based on objects — software entities that combine data and the functions that operate on that data. Instead of structuring a program as a list of instructions and separate data (the procedural style), OOP models a program as a collection of objects that interact with one another. Each object encapsulates state (its data) and behavior (its methods), and external code communicates with it through a public interface.
The short version: an OOP program is made of objects; an object bundles data and behavior; classes and prototypes provide blueprints for objects; and four core ideas — encapsulation, inheritance, polymorphism, and abstraction — give the paradigm its power. It is the dominant model in languages like Java, C++, C#, Python, and JavaScript, and it underpins most large software systems you use today.
Where OOP came from
The idea of "objects" in programming has deep roots. At MIT in the late 1950s and early 1960s, researchers used "object" to refer to LISP atoms with identified properties. Ivan Sutherland's Sketchpad (1960–1961) defined terms like "object" and "instance" in the context of graphical interaction. Meanwhile, in Norway, the Simula language was developed during 1961–1967 and introduced the essential object-oriented ideas: classes, inheritance, and dynamic binding. Simula is generally accepted as the first language with the primary features of an object-oriented language; it was used mainly for physical modeling, such as simulating the movement of ships through cargo ports.
Alan Kay, influenced by both MIT and Simula, began developing his own ideas in 1966 and went on to create Smalltalk at Xerox PARC in the 1970s — a language that treated objects as the fundamental building blocks of a dynamic system. Kay described objects as being "like biological cells and/or individual computers on a network, only able to communicate with messages." OOP rose to prominence in the late 1970s and 1980s, and by the 1990s it had become the main way of programming, especially as graphical user interfaces — which use objects for buttons, menus, and other elements — became widespread.
Objects, classes, and instances
The central unit of OOP is the object. An object combines:
- State — the data it holds, stored in fields (also called attributes or properties), often as variables.
- Behavior — the functions (called methods) that define what the object can do.
In class-based programming, the most common form of OOP, an object is an instance of a class. The class is the blueprint: it defines the variables and methods that every instance will have. An object is created through the class's constructor. Every instance of a class has the same set of variables and methods, but each instance holds its own values for the instance variables.
In JavaScript, you declare a class with the class keyword and create instances with new:
class Person {
name;
constructor(name) {
this.name = name;
}
introduceSelf() {
console.log(`Hi! I'm ${this.name}`);
}
}
const giles = new Person("Giles");
giles.introduceSelf(); // Hi! I'm Giles
Some languages — most notably JavaScript — also support prototype-based programming, where instead of a class concept, an object is linked to another object called its prototype. A prototype acts as a model for new objects: create one fruit object, then make apple and orange that share fruit's traits. In JavaScript each object has exactly one prototype link, up to a base object whose prototype is null. JavaScript's class syntax is a convenience layer that still works through prototypes under the hood.
Encapsulation and information hiding
Encapsulation is the practice of bundling related data and methods together in an object, and information hiding is the related idea of keeping an object's internal details hidden from outside code. They work together through several mechanisms:
- Cohesion — keeping related fields and methods together in one place.
- Decoupling — organizing code so that only certain parts of the data are used by related functions, making it easier to change how an object works internally without affecting other parts of the codebase.
- Data hiding — consuming code can only interact with an object through its public members, controlled by the language's access modifiers.
Languages differ in how they expose visibility. Java provides private and public keywords; protected allows access from the same class and its subclasses. Python has no visibility keyword, but developers follow a convention such as starting a "private" member name with an underscore. In JavaScript, you can declare private fields whose names start with # — code outside the object that tries to access them throws an error.
class Student extends Person {
#year; // private field — inaccessible outside the class
constructor(name, year) {
super(name);
this.#year = year;
}
canStudyArchery() {
return this.#year > 1;
}
}
Encapsulation lets you change an object's internals — the rules, the storage, the logic — without breaking the code that uses it, because that code only depends on the public interface.
Inheritance
Inheritance is the mechanism of basing a class or object on another, so that a child retains the implementation of its parent. In class-based languages, a class may inherit from another class, creating a hierarchy: a subclass inherits from a super-class. For example, an Employee class might inherit from a Person class, endowing the Employee object with Person's variables. The subclass can add its own variables and methods without affecting the super-class, and most languages let it override super-class methods.
In JavaScript this is done with the extends keyword. A subclass constructor must first call the superclass constructor with super():
class Professor extends Person {
teaches;
constructor(name, teaches) {
super(name);
this.teaches = teaches;
}
introduceSelf() {
console.log(`My name is ${this.name}, and I will be your ${this.teaches} professor.`);
}
}
Languages may also support multiple inheritance (inheriting from more than one class), or alternatives like mixins and traits. Some OOP languages — for example Go — deliberately do not support inheritance at all, and instead encourage composition over inheritance: build objects from smaller parts rather than parent–child relationships. An Employee class can simply contain a Person object instead of inheriting from it, which gives the class fine-grained control over how much of Person it exposes.
Opinions on inheritance vary widely. Bjarne Stroustrup, author of C++, has said it is possible to do OOP without inheritance, and Rob Pike has criticized inheritance for creating complex hierarchies instead of simpler solutions.
Polymorphism and dynamic dispatch
Polymorphism (specifically subtype polymorphism) lets a function work with a specific interface and thus manipulate entities of different classes in a uniform way. Imagine a program with two shapes, a circle and a square, both inheriting from a common "Shape" class. Each shape draws itself. With subtype polymorphism, the program doesn't need to know each shape's concrete type — it simply calls the "Draw" method on each, and the runtime ensures the correct version runs. Because each shape's details are handled inside its own class, this yields strong separation of concerns.
Related to this is dynamic dispatch: a method may be selected at runtime rather than compile time. Dynamic dispatch works together with inheritance — if an object doesn't have the requested method, it looks up its parent class and continues up the chain to find a match. A method call can also be thought of as message passing: the method name and its inputs are like a message sent to the object for it to act on.
Abstraction and open recursion
Abstraction is the general OOP idea of exposing only what's necessary while hiding complexity — treating an object as a black box with a known interface. Some languages support abstract classes, which cannot be directly instantiated and exist only to be subclassed.
Most OOP languages also provide a special value, this or self, that refers to the current object inside its methods. In languages that support open recursion, a method in an object can call other methods on the same object — including itself — using this. This permits a method defined in one class to invoke another method that is only defined later in a subclass, a feature known as late binding.
OOP in the real world
Objects sometimes directly model real-world things: a graphics program has circle, square, and menu objects; an online shopping system has shopping cart, customer, and product objects. But more often, objects represent abstract entities, like an open file or a unit converter. Niklaus Wirth said that OOP "closely reflects the structure of systems in the real world and is therefore well suited to model complex systems with complex behavior." Not everyone agrees — critics such as Joe Armstrong noted that OOP carries "all this implicit environment," and some argue OOP doesn't always enhance readability or modularity.
Design patterns — common, reusable solutions to software design problems — are typically introduced in an OOP context. Common object patterns include the immutable object (which never changes state after creation), the factory object (which creates other objects), and the singleton (the only instance of its class for the program's lifetime). A well-known OOP anti-pattern is the God object — an object that knows or does too much.
FAQ
What is the difference between a class and an object?
A class is a blueprint or template that defines the variables and methods a set of objects will have. An object is a concrete instance of that class, created (usually) through a constructor. Every object of a class shares the same set of variables and methods, but each instance holds its own values for its instance variables.
Is JavaScript an object-oriented language?
Yes. JavaScript supports object-oriented programming — primarily prototype-based OOP, where objects are linked to a prototype that acts as a model. JavaScript also provides class syntax with extends inheritance and #-prefixed private fields, which map more closely to classical OOP concepts, even though they still work through prototypes under the hood.
What are the four pillars of OOP?
The four core ideas are encapsulation (bundling data and methods together and hiding internals), abstraction (exposing only what's necessary), inheritance (basing a class on another to reuse its implementation), and polymorphism (manipulating objects of different classes through a common interface, backed by dynamic dispatch).
Is composition better than inheritance?
It depends on the problem. Composition means an object contains other objects (a "has-a" relationship) rather than inheriting from them (an "is-a" relationship). Languages like Go skip inheritance entirely and encourage composition over inheritance. Many developers prefer composition because it gives fine-grained control and avoids brittle, deep class hierarchies, but inheritance remains a powerful and widely used tool where a genuine parent–child relationship exists.
Summary
Object-oriented programming organizes a program around objects that combine state and behavior. Class-based languages use classes as blueprints and create instances through constructors; prototype-based languages like JavaScript link objects to a prototype. Four core ideas give OOP its shape: encapsulation hides internal details behind a public interface, inheritance reuses implementation through class hierarchies, polymorphism lets one interface work uniformly across different classes (with dynamic dispatch selecting the right method at runtime), and abstraction exposes only what's necessary. OOP underpins most modern languages and large software systems, and understanding its principles is a foundation skill for any developer.
Sources
- Wikipedia — Object-oriented programming (en.wikipedia.org/wiki/Object-oriented_programming
- CC BY-SA 4.0)
- Wikipedia — Inheritance (object-oriented programming) (en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)
- CC BY-SA 4.0)
- MDN — Classes in JavaScript (developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Classes_in_JavaScript
- CC-BY-SA 2.5+)
- MDN — Object-oriented programming (developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object-oriented_programming
- CC-BY-SA 2.5+)
Share this guide
Related guides
- What is a Regular Expression (regex)? A practical guide
A regular expression (regex or regexp) is a sequence of characters that forms a search pattern for matching and manipulating text. Built on a formal mathematical foundation and common to almost every programming language and tool, regex lets you validate input, find and replace text, extract data, and transform strings with a compact, declarative syntax. This guide explains what regex is, its core syntax, how it is implemented, and how to use it safely.
- What is WebAssembly (WASM)? How it works and when to use it
WebAssembly is a binary instruction format for a stack-based virtual machine — a portable compilation target that lets you run C, C++, Rust, and other compiled languages in the browser and beyond. Here's how it works and when it's worth using.
- What is unit testing? How to write, structure, and run unit tests
Unit testing is the practice of testing the smallest testable parts of a program — functions, methods, or classes — in isolation. Learn what a unit test is, the arrange-act-assert structure, test doubles and mocking, the testing pyramid, and best practices for writing good unit tests.