An object is a collection of properties, and a property is an association between a name (or key) and a value.
'Working with Objects' - MDN
- In JavaScript, almost everything is an object.
- At its core, an object is a standalone collection of related data and/or functionality, consisting of key-value pairs (known as properties and methods).
- An object literal is wrapped in curly braces (
{}) and contains comma-separated key-value pairs:
There are two different syntaxes for accessing the properties of an object:
- Allows dynamic keys, spaces, or special characters via strings or variables.
- Other than object literals (
{}), in JavaScript there are various different ways to instantiate objects:
Prototypes are the mechanism by which JavaScript objects inherit features from one another.
'Prototypes' - MDN
- JavaScript is a prototype-based language.
- This means that JavaScript handles inheritance differently from classic object-oriented languages like Java, C++ or Python.
- Instead of inheriting from "classes" that act as rigid blueprints, JavaScript objects inherit directly from other objects.
- Every object in JavaScript has a hidden internal property (historically referred to as
[[prototype]] or accessed via __proto__) that links to another object called its prototype.
JavaScript implements inheritance by using objects. Each object has an internal link to another object called its prototype. That prototype object has a prototype of its own, and so on until an object is reached with null as its prototype. By definition, null has no prototype and acts as the final link in this prototype chain.
'Inheritance and the prototype chain' - MDN
When you try to access a property or method on an object:
- JavaScript will first check the object itself for that property/method. If it is there, it will use it.
- If not, it looks up the prototype link to the other object's prototype (
__proto__).
- If it is not there again, it will check that objects prototype, continually moving up the chain until it reaches
Object.prototype whose prototype is null.
- When the
class syntax was introduced in ES6, it didn't add a traditional class-based inheritance model (as seen in other programming languages like Java, C++, Python, etc).
- Instead, the
class keyword is just syntactic sugar built on top of JavaScript's existing prototype-based system.
- The
extends keyword is used in class declarations or class expressions to create a child class that inherits all the properties and methods of the parent class.
- It sets up the prototype chain so that instances of the child class have access to the parent class’s methods.
Example:
The super keyword
The super keyword serves two purposes depending on where it is being used:
- When a child class defines its own constructor, it must call
super before it can use the this keyword.
- JavaScript requires the parent class to initialise its own inherited properties first. Passing arguments into super(...) forwards them to the parent class’s constructor.
JavaScript getters and setters are special methods used to access and update object properties. They provide a controlled way to read and modify values.
'JavaScript Getters and Setters' - GeeksforGeeks
- Instead of directly interacting with a raw variable, getters and setters let you intercept the read (getting) and write (setting) operations.
- They use the
get and set keywords.
- Data Validation: You can ensure that assigned values meet certain conditions (e.g., a user's age cannot be negative).
- Computed Properties: You can dynamically calculate a value on the fly whenever it's requested rather than storing it statically.
- Encapsulation: You can hide internal implementation details while presenting a clean property interface to the outside code.
Example - Validation:
Example - Computed Properties:
Example - Getters and Setters with classes:
Encapsulation is the packing of data and functions into one component (for example, a class) and then controlling access to that component to make a "blackbox" out of the object. Because of this, a user of that class only needs to know its interface (that is, the data and functions exposed outside the class), not the hidden implementation.
'Encapsulation' - MDN
- To put it simply, encapsulation is about bundling data and the methods that operate on that data within a single unit (an object) and restricting direct access to some of the objects internal components.
Encapsulation gives you:
- Data Protection: Prevents external code from accidentally breaking an objects internal logic.
- Controlled Access: Enforces validation rules (e.g., ensuring an age cannot be a negative number) via methods or getters/setters.
- Abstraction: Exposes only what other developers need to use
Encapsulation pre-ES2022
Before native private fields were introduced, JavaScript developers relied on conventions and closures to hide data.
- Developers would use the
_ convention to indicate not to use particular properties.
Example:
- The issue was that the field was not actually hidden. Nothing was actually preventing external code from reading or altering the property.
- To truly hide fields, developers would use function scopes and closures.
Example:
- The issue with this pattern is that every instance duplicates the methods in memory, which can be inefficient for large applications compared to prototype methods.
JavaScript introduced a native syntax for private fields, methods and static members using the hash (#) prefix.
Private elements get created by using a hash # prefix and cannot be legally referenced outside of the class.
The privacy encapsulation of these class elements is enforced by JavaScript itself.
The only way to access a private element is via dot notation, and you can only do so within the class that defines the private element.
'Private Elements' - MDN
Example:
- Declaration is mandatory: Private fields must be declared at the top of the class body (e.g.,
#balance;). You cannot dynamically create them inside methods or constructor arguments without declaring them first.
- Strict runtime enforcement: Attempting to access or modify a private field outside of the class body results in a syntax error.
- Private methods and static fields: You can also make methods and static properties private.
Example:
One of the most famous architectural debates in software engineering is inheritance vs composition. While JavaScript historically leaned into prototypal delegation that looked like classical inheritance (especially with the introduction of ES6 class syntax), modern JavaScript development heavily favours composition.
- To understand the core difference between inheritance and composition we can look at how each model answers the question of relationship:
- Inheritance ("Is-A"): A subclass is a specialised version of a superclass.
- Example: A
Dog is an Animal. A Manager is an Employee.
- Composition ("Has-A" or "Can-Do"): An object is built by combining independent, modular pieces of behaviour or smaller objects.
- Example: A
Robot has a battery, has a navigationSystem, and can walk. It doesn't inherit from a generic Machine tree - it is assembled from parts.
Inheritance feels intuitive at first because it easily maps on to real world taxonomies. However, as applications grow, deep inheritance hierarchies begin to cause severe maintenance issues.
Issues with deep inheritance:
- Tight coupling: Subclasses are deeply bound to their parent classes. Changing a method deep in the superclass can inadvertently break dozens of subclasses.
- The Fragile Base Class Problem: If a base class is modified by adding a new method or property, it can clash with existing subclass methods or introduce unexpected behaviors.
- Inflexible Taxonomies (The "Flying Dog" Dilemma):
Imagine you start with a class hierarchy:
Animal → Bird (adds fly()) and Animal → Dog (adds bark()). What happens when you need to create a RobotDog that can walk and talk, or a FlyingSquirrel that can glide, or a SuperHero that inherits traits from both human and ? Inheritance forces you into rigid, single-parent hierarchies that cannot easily accommodate cross-cutting behaviours.
Composition in JavaScript is an object-oriented programming concept in which one object is built using other objects as its parts. It represents a strong “has-a” relationship, where the contained objects are closely tied to the lifecycle of the parent object.
'Composition in JavaScript' - GeeksforGeeks
Instead of asking "What is this object?", composition asks "What does this object do?" or "What parts make up this object?".
- In JavaScript, because objects are dynamic collections of key-value pairs, you can easily assemble objects by mixing and matching independent functions.
- You can create functional mixins - functions that take an object, argument it with new methods and return it.
- Inheritance should be used sparingly when you have a genuine, shallow "Is-A" relationship.
- For almost everything else, you should be using composition with factory functions or mixins.
Comments (0)