OOP#
JavaScript’s object model is prototype-based. Every object
has a prototype chain that is walked on property reads. ES2015
class is sugar over that mechanism, with ergonomics
(extends, super, static, #private, get /
set) that make the prototype machinery invisible most days.
For the function surface methods sit on, see
Functions. For Object, Map, Set, and other
container objects, see Data Structures.
Plain objects#
The shortest path to an object is the object literal. Keys are strings or symbols; values are anything.
const point = {x: 1, y: 2};
point.x; // 1
point["x"]; // same
const k = "x"; point[k]; // same
const o = {
[`key_${i}`]: "computed", // computed key
greet(name) { return `hi, ${name}`; }, // method shorthand
};
Spread and Object.assign shallow-copy.
const merged = {...a, ...b};
const copy = Object.assign({}, original);
const frozen = Object.freeze({...defaults, ...overrides});
Classes#
class declares a constructor function plus a prototype with
the methods attached.
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
distance(other) {
const dx = this.x - other.x;
const dy = this.y - other.y;
return Math.hypot(dx, dy);
}
}
const p = new Point(0, 0);
const q = new Point(3, 4);
p.distance(q); // 5
Method shorthand (distance(other) { ... }) puts the function
on Point.prototype; that prototype is shared across instances.
Inheritance#
extends chains prototypes; super(...) calls the parent
constructor; super.method(...) calls the parent method.
class Animal {
constructor(name) { this.name = name; }
speak() { console.log(`${this.name} makes a sound`); }
}
class Dog extends Animal {
speak() {
super.speak();
console.log(`${this.name} barks`);
}
}
new Dog("rex").speak();
Multiple inheritance is not native; mixins (functions that extend a class) cover the common cases.
const Loggable = Base => class extends Base {
log(msg) { console.log(`[${this.name}] ${msg}`); }
};
class Server extends Loggable(Animal) {}
Visibility#
#name declares a private field (or method). Private names
are truly inaccessible from outside the class, enforced by the
runtime.
class Cache {
#data = new Map(); // private field
#hits = 0;
get(k) {
const v = this.#data.get(k);
if (v !== undefined) this.#hits++;
return v;
}
stats() { return {hits: this.#hits, size: this.#data.size}; }
}
const c = new Cache();
c.#data; // SyntaxError; private outside class
static puts a method or field on the class itself, not the
prototype.
class Server {
static DEFAULT_PORT = 8080;
static create() { return new Server(); }
}
Server.create();
Accessors#
get and set give read / write semantics that look like
properties but run code.
class Temp {
constructor(c) { this._c = c; }
get fahrenheit() { return this._c * 9/5 + 32; }
set fahrenheit(f) { this._c = (f - 32) * 5/9; }
}
const t = new Temp(100);
t.fahrenheit; // 212
t.fahrenheit = 32; // sets _c via the setter
this binding#
this inside a method is the receiver of the call.
const p = new Point(1, 2);
p.distance(q); // this = p inside distance
const fn = p.distance;
fn(q); // TypeError; this is undefined
Bind a method if it must be passed around standalone.
const bound = p.distance.bind(p);
bound(q); // works
Arrow methods on class fields capture this lexically (useful
for event handlers).
class Component {
constructor() {
this.label = "go";
}
onClick = () => console.log(this.label); // safe to pass around
}
Prototypes#
A class is the constructor function plus a prototype object.
Object.getPrototypeOf(obj) walks the chain; obj.__proto__
is the legacy accessor for the same thing.
Object.getPrototypeOf(p) === Point.prototype; // true
Object.getPrototypeOf(Point.prototype) === Object.prototype; // true
Point.prototype.translate = function (dx, dy) {
this.x += dx; this.y += dy;
};
p.translate(1, 1); // method monkey-patched
The operator monkey-patches sparingly; it confuses tooling and breaks under module re-evaluation.
instanceof#
x instanceof C walks the prototype chain looking for
C.prototype. Works for built-ins too.
[] instanceof Array; // true
"x" instanceof String; // false (primitive)
new Date() instanceof Date; // true
Across realm boundaries (iframe, worker), prototypes differ;
Array.isArray(x) is the cross-realm-safe array check.
References#
Functions for the call mechanics
thisbinding uses.Types for
objectandtypeofresults.Data Structures for
Map,Set,WeakMap, and built-in container classes.