---
title: 'Prototypal Inheritance'
source: 'https://academia.sh/en/courses/javascript-object-model/prototypal-inheritance'
course: 'Objects and Functions in JavaScript'
language: en
updated: '2026-08-23T07:01:00+00:00'
license: 'CC BY-SA 4.0'
---

# Prototypal Inheritance

The link constructor functions and the new operator set up, extending the prototype chain to two layers, type-query operations, and the link changing at runtime.

The previous lesson set up the prototype link by hand: shared behavior was gathered in
one object, each record was bound to it with `Object.create`, and fields were filled in
one by one. The pattern works, but it has two gaps. Filling in fields and binding
behavior sit in separate places, and there is no built-in setup for deriving one record
type from another.

The language offers a built-in path for both. This lesson opens that path — the
constructor function and the `new` operator — then writes by hand what `new` does, to
show that it is not magic but a shorthand for the three steps you saw in the previous
lesson.

## The Constructor Function

Every function in JavaScript carries an ordinary property named `prototype`. This
property is **not** the function's own prototype; it is the object that will become the
prototype of the objects the function produces when called with `new`. Because the name
similarity hides this distinction, the two concepts must not be confused.

A function written to be called with `new` is called a **constructor function**. It
fills in fields through `this`, while behaviors are written to the `prototype` object.

```js
function MeasurementRecord(sensor, value, time) {
  this.sensor = sensor;
  this.value = value;
  this.time = time;
}

MeasurementRecord.prototype.format = function () {
  return `${this.sensor}@${this.time}: ${this.value}`;
};

MeasurementRecord.prototype.exceedsThreshold = function (threshold) {
  return this.value > threshold;
};

const recordOne = new MeasurementRecord("S-01", 21.4, 1000);
const recordTwo = new MeasurementRecord("S-02", 19.8, 1060);

console.log(recordOne.format());
console.log(Object.getPrototypeOf(recordOne) === MeasurementRecord.prototype);
console.log(recordOne.format === recordTwo.format);
console.log(Object.keys(recordOne).join(","));
console.log(recordOne.constructor === MeasurementRecord);
console.log(Object.hasOwn(MeasurementRecord.prototype, "constructor"));
```

```
S-01@1000: 21.4
true
true
sensor,value,time
true
true
```

The result is the same as the previous lesson's setup: data separate in each record,
behavior in one place. The fourth line confirms this — the record's own properties are
only the three data fields.

The fifth and sixth lines expose a detail. Every function's `prototype` object, the
moment it is created, carries a property named `constructor`, and this property points
at the function itself. The expression `recordOne.constructor` is not found on the
record; it comes from the chain, through `MeasurementRecord.prototype`. So `constructor`
is not an object's "true type," it is an ordinary property encountered on its chain — the
cost of this distinction will be seen in the next section.

## What the new Operator Does

The expression `new MeasurementRecord(...)` breaks down into four steps:

1. An empty object is created whose prototype is `MeasurementRecord.prototype`.
2. The constructor function is called with `this` bound to this object.
3. If the constructor function returns an object, that object is the result; if not, the
   object from the first step is the result.
4. The result becomes the value of the expression.

All of these steps are ordinary operations; none of them carries language-specific
privilege. A function doing the same job can be written.

```js
function MeasurementRecord(sensor, value, time) {
  this.sensor = sensor;
  this.value = value;
  this.time = time;
}
MeasurementRecord.prototype.format = function () {
  return `${this.sensor}@${this.time}: ${this.value}`;
};

function newInstance(Constructor, ...args) {
  const obj = Object.create(Constructor.prototype);
  const result = Constructor.apply(obj, args);
  return typeof result === "object" && result !== null ? result : obj;
}

const viaHelper = newInstance(MeasurementRecord, "S-01", 21.4, 1000);
const viaOperator = new MeasurementRecord("S-01", 21.4, 1000);

console.log(viaHelper.format());
console.log(Object.getPrototypeOf(viaHelper) === MeasurementRecord.prototype);
console.log(JSON.stringify(viaHelper) === JSON.stringify(viaOperator));
console.log(viaHelper instanceof MeasurementRecord);
```

```
S-01@1000: 21.4
true
true
true
```

The `newInstance` function stands in for the `new` operator and produces an
indistinguishable result. The `apply` call explicitly sets up the `this` binding; this
operation's rules will be covered in the first two lessons of the Context and Closures
topic. The real observation here is this: `new` is a shorthand laid on top of the
prototype model, not a separate mechanism.

The `newInstance` function we wrote does not cover the whole of `new`: constructors
defined with class syntax cannot be called without `new`, and details like `new.target`
fall outside this shorthand. What matters for the model is the three lines above.

## Extending the Chain

Consider a subtype of measurement records: a **calibrated measurement** carrying a
sensor's systematic drift. Its fields cover the measurement record's fields and add an
offset value on top; how the read value is computed differs, how it is formatted is the
same.

Extending the chain to two layers requires two jobs: the subtype's prototype has to be
bound to the supertype's prototype, and while the sub-constructor runs, the
super-constructor's field-filling job has to run too.

```js
function MeasurementRecord(sensor, value, time) {
  this.sensor = sensor;
  this.value = value;
  this.time = time;
}
MeasurementRecord.prototype.format = function () {
  return `${this.sensor}@${this.time}: ${this.value}`;
};
MeasurementRecord.prototype.reading = function () {
  return this.value;
};

function CalibratedMeasurement(sensor, value, time, offset) {
  MeasurementRecord.call(this, sensor, value, time);
  this.offset = offset;
}

CalibratedMeasurement.prototype = Object.create(MeasurementRecord.prototype);
CalibratedMeasurement.prototype.constructor = CalibratedMeasurement;

CalibratedMeasurement.prototype.reading = function () {
  return this.value + this.offset;
};

const calibrated = new CalibratedMeasurement("S-03", 21.4, 1120, -0.4);

console.log(calibrated.format());
console.log(calibrated.reading());
console.log(MeasurementRecord.prototype.reading.call(calibrated));
console.log(Object.getPrototypeOf(calibrated) === CalibratedMeasurement.prototype);
console.log(Object.getPrototypeOf(CalibratedMeasurement.prototype) === MeasurementRecord.prototype);
console.log(calibrated.constructor === CalibratedMeasurement);
```

```
S-03@1120: 21.4
21
21.4
true
true
true
```

The chain now has four links: `calibrated` → `CalibratedMeasurement.prototype` →
`MeasurementRecord.prototype` → `Object.prototype` → `null`. `format` is not found on the
second link, it is found on the third; `reading` is found on the second link, so it
shadows the third's definition. The **polymorphism** introduced in the Introduction to
Object-Oriented Programming lesson of the Programming Fundamentals course is exactly this
shadowing here: the same name corresponds to different implementations at different
layers of the chain, and lookup order decides which one runs.

Three lines deserve attention. `MeasurementRecord.call(this, ...)` runs the
super-constructor's field-filling job on the new object. If the link set up with
`Object.create(MeasurementRecord.prototype)` had instead been set up as
`CalibratedMeasurement.prototype = MeasurementRecord.prototype`, the two types would
share the exact same prototype object, and every behavior added to the subtype would leak
into the supertype too. Writing the `constructor` property back by hand is the
consequence of the first section's observation: when the `prototype` object is replaced,
`constructor` is lost along with it, and if it is not rewritten, `calibrated.constructor`
finds `MeasurementRecord` from the chain.

## Type Querying on the Chain

The `instanceof` operator asks whether the function on its right's `prototype` object is
found on the chain of the object on its left. This query, which looks like class
membership, is actually a chain scan.

```js
function MeasurementRecord(sensor, value) {
  this.sensor = sensor;
  this.value = value;
}
function CalibratedMeasurement(sensor, value, offset) {
  MeasurementRecord.call(this, sensor, value);
  this.offset = offset;
}
CalibratedMeasurement.prototype = Object.create(MeasurementRecord.prototype);
CalibratedMeasurement.prototype.constructor = CalibratedMeasurement;

const calibrated = new CalibratedMeasurement("S-03", 21.4, -0.4);
const plain = new MeasurementRecord("S-01", 21.4);

console.log(calibrated instanceof CalibratedMeasurement);
console.log(calibrated instanceof MeasurementRecord);
console.log(calibrated instanceof Object);
console.log(plain instanceof CalibratedMeasurement);

console.log(MeasurementRecord.prototype.isPrototypeOf(calibrated));
console.log(CalibratedMeasurement.prototype.isPrototypeOf(plain));

let stop = calibrated;
const chain = [];
while (stop !== null) {
  stop = Object.getPrototypeOf(stop);
  chain.push(stop === null ? "null" : stop.constructor.name);
}
console.log(chain.join(" -> "));
```

```
true
true
true
false
true
false
CalibratedMeasurement -> MeasurementRecord -> Object -> null
```

The last block walks the chain from start to end and prints it, making visible where the
`instanceof` results come from. Because all three `prototype` objects are found on the
calibrated measurement's chain, all three queries answer positively; because the plain
record does not carry the subtype's prototype on its chain, the fourth query is negative.
`isPrototypeOf` asks the same question from the reverse direction and looks not at the
function but directly at the prototype object.

## The Liveness of the Link

The prototype link is not a copy, it is a live reference. A property added to the
prototype after an object is created immediately becomes visible on every existing
object.

```js
function MeasurementRecord(sensor, value) {
  this.sensor = sensor;
  this.value = value;
}
MeasurementRecord.prototype.format = function () {
  return `${this.sensor}: ${this.value}`;
};

const record = new MeasurementRecord("S-01", 21.4);
console.log(record.format());
console.log(typeof record.summarize);

MeasurementRecord.prototype.summarize = function () {
  return `${this.sensor} record`;
};

console.log(typeof record.summarize);
console.log(record.summarize());

const otherBehavior = {
  format() {
    return `[${this.sensor}] ${this.value}`;
  },
};
Object.setPrototypeOf(record, otherBehavior);
console.log(record.format());
console.log(record instanceof MeasurementRecord);
console.log(typeof record.summarize);
```

```
S-01: 21.4
undefined
function
S-01 record
[S-01] 21.4
false
undefined
```

`summarize` was added after the record was created and becomes accessible with no
re-creation at all; because the search is done fresh on every read, the link is live.

`Object.setPrototypeOf` changes an existing object's link. The consequences are broad:
the record no longer sees the supertype's behaviors, the `instanceof` result flips, and
because runtimes optimize based on object shapes, access performance drops. For this
reason the link is set up at the moment the object is created and is not changed
afterward; `Object.create` or `new` exist for exactly this.

## Summary

- Every function's `prototype` property is the object that will become the prototype of
  the objects it produces with `new`; it is not the function's own prototype.
- `new` creates an empty object whose prototype is bound to the `prototype` object, runs
  the constructor on this object, and returns the result; the same job can be written by
  hand with `Object.create`.
- Extending the chain consists of two jobs: binding the sub-prototype to the
  super-prototype, and calling the super-constructor from inside the sub-constructor.
- A same-named definition at a lower layer shadows the one above it; polymorphism is the
  result of this lookup order.
- `instanceof` and `isPrototypeOf` ask not about type membership but whether a prototype
  object is found on the chain.
- The link is live: properties added to the prototype afterward become visible on
  existing objects.

## Next Step

In this lesson the chain was set up by hand, and every step of the setup was made
visible: the `prototype` assignment, the `constructor` repair, the super-constructor
call. All three steps being easy to forget is the reason a syntax was added to the
language that gathers them into a single declaration. The next lesson introduces class
syntax and shows, with the same tests, which operation from this lesson each of its lines
corresponds to.
