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

# Object Prototype

The prototype link every object carries, the order in which properties are searched through this link, the distinction between an own property and an inherited property, and where the chain ends.

The JavaScript Fundamentals course introduced the object as a key–value mapping:
properties are written, read, deleted. This definition is enough for everyday use, but it
leaves one question unanswered. Create an empty object and read its `toString` property
and you get not `undefined` but a function. No one wrote that function there.

The answer stands at the center of the language's object model: every object carries, in
addition to its own properties, **a link to another object**. If the property you are
looking for is not on the object itself, the search continues through this link. This
lesson builds that link; the rest of the course — including class syntax — sits on top of
it.

## Two Copies of the Same Behavior

Throughout this course we will work with a single concrete example: a **measurement
record** read from a sensor. A record carries three pieces of data (sensor id, measured
value, moment of measurement) and offers at least one behavior (printing itself in
readable form). The most direct setup is to write each record on its own as an object
literal.

```js
const recordOne = {
  sensor: "S-01",
  value: 21.4,
  time: 1000,
  format() {
    return `${this.sensor}@${this.time}: ${this.value}`;
  },
};

const recordTwo = {
  sensor: "S-02",
  value: 19.8,
  time: 1060,
  format() {
    return `${this.sensor}@${this.time}: ${this.value}`;
  },
};

console.log(recordOne.format());
console.log(recordTwo.format());
console.log(recordOne.format === recordTwo.format);
```

```
S-01@1000: 21.4
S-02@1060: 19.8
false
```

The last line makes the problem visible. Both records offer the same behavior, but in
memory there are **two separate function objects**; the identity comparison says so. A
thousand measurements produce a thousand copies, and when the formatting rule changes, it
has to change in a thousand places.

The Introduction to Object-Oriented Programming lesson in the Programming Fundamentals
course answered this problem with the class concept: behavior is written once in the
class, objects share it. JavaScript's answer sits one level lower and requires no class.

## The Prototype Link

Every object, the moment it is created, is bound to another object — or to the value
`null`. This link is called the **prototype**. The link is not an ordinary property of
the object; in the language's specification it appears as an internal field named
`[[Prototype]]`, and it is read with the `Object.getPrototypeOf` function.

`Object.create` sets up the link directly: it produces a new object whose prototype is
the given object.

```js
const measurementBehavior = {
  format() {
    return `${this.sensor}@${this.time}: ${this.value}`;
  },
  exceedsThreshold(threshold) {
    return this.value > threshold;
  },
};

function measurementRecord(sensor, value, time) {
  const record = Object.create(measurementBehavior);
  record.sensor = sensor;
  record.value = value;
  record.time = time;
  return record;
}

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

console.log(recordOne.format());
console.log(recordTwo.exceedsThreshold(20));
console.log(recordOne.format === recordTwo.format);
console.log(Object.getPrototypeOf(recordOne) === measurementBehavior);
```

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

The third line now gives `true`: both records use the same function object, because the
function stands not in the records but in the shared prototype. The fourth line tests the
link itself — every claim about the object model throughout this lesson will be shown
this way, with a directly testable comparison.

Data is separate in each record, behavior is in one place. This split is the entire point
of the prototype model: **the part that changes stands in the object, the part that does
not stands in the chain.**

## Property Lookup Order

When you read a property, the runtime follows this order:

1. Search among the object's own properties. If found, return the value.
2. If not found, move to its prototype and do the same search there.
3. Continue until the chain reaches `null`. If found nowhere, return `undefined`.

Because the search stops at the first match, an object's own property **shadows** a
prototype property of the same name. The Scope and Lifetime lesson in the Programming
Foundations course described an inner scope shadowing a name in an outer scope; the
mechanism here is the same idea's counterpart for objects.

```js
const measurementBehavior = {
  format() {
    return `${this.sensor}@${this.time}: ${this.value}`;
  },
};

const record = Object.create(measurementBehavior);
record.sensor = "S-01";
record.value = 21.4;
record.time = 1000;

console.log(record.format());

record.format = function () {
  return `[raw] ${this.value}`;
};

console.log(record.format());
console.log(Object.hasOwn(record, "format"));
console.log(measurementBehavior.format.call(record));

delete record.format;
console.log(record.format());
```

```
S-01@1000: 21.4
[raw] 21.4
true
S-01@1000: 21.4
S-01@1000: 21.4
```

Three observations matter. First, assignment does not **change** the prototype's
function; it creates a new property belonging to the record. A write never climbs the
chain, it always writes to the object itself. Second, the shadowed property does not
disappear; it can still be called with `call` (the details of this operation belong to
the Context and Closures topic). Third, when the own property is deleted, the chain
becomes visible again — deletion looks as if it brought back the prototype's definition,
but it actually only removed the shadow.

## Own Property and Inherited Property

Properties coming through the chain are not distinguished from own properties in most
read operations; the places the distinction matters are counting and serialization.

```js
const measurementBehavior = {
  format() {
    return `${this.sensor}@${this.time}: ${this.value}`;
  },
};

const record = Object.create(measurementBehavior);
record.sensor = "S-01";
record.value = 21.4;
record.time = 1000;

console.log("format" in record);
console.log(Object.hasOwn(record, "format"));
console.log(Object.keys(record).join(","));

const forInKeys = [];
for (const key in record) {
  forInKeys.push(key);
}
console.log(forInKeys.join(","));

console.log(JSON.stringify(record));
```

```
true
false
sensor,value,time
sensor,value,time,format
{"sensor":"S-01","value":21.4,"time":1000}
```

The `in` operator looks at the whole chain, `Object.hasOwn` only at the object itself.
`Object.keys` is limited to own properties; the `for...in` loop also walks properties
coming from the chain, which is why it is not a reliable tool for iterating over an
object. `JSON.stringify` also works with own properties only — serialization takes only
the data, leaves the behavior behind. This behavior gives an early hint of the
distinction at the end of the lesson: **an object's data is in itself, its identity is in
its chain.**

The `format` name appearing in the `for...in` output here is because the prototype
property is *enumerable*. How enumerability is controlled is the subject of the Property
Descriptors lesson; class syntax's difference on this point will be shown there too.

## The End of the Chain

The chain is not infinite. Every object created with an object literal has
`Object.prototype` as its prototype, and that object's prototype is `null`. This is why
names like `toString`, `valueOf`, and `hasOwnProperty` are found on every object.

```js
const measurementBehavior = {
  format() {
    return `${this.sensor}@${this.time}: ${this.value}`;
  },
};
const record = Object.create(measurementBehavior);

console.log(Object.getPrototypeOf(record) === measurementBehavior);
console.log(Object.getPrototypeOf(measurementBehavior) === Object.prototype);
console.log(Object.getPrototypeOf(Object.prototype));

console.log(typeof record.toString);
console.log(typeof record.unknownProperty);

const dictionary = Object.create(null);
dictionary.sensor = "S-01";
console.log(Object.getPrototypeOf(dictionary));
console.log(typeof dictionary.toString);
console.log("toString" in dictionary);
```

```
true
true
null
function
undefined
null
undefined
false
```

The chain has three links: `record` → `measurementBehavior` → `Object.prototype` →
`null`. When a nonexistent property is requested, the runtime walks all three stops and
returns `undefined`; it does not throw an error. Search cost grows with chain length, but
the work done at each stop is a dictionary lookup — exactly the average constant-cost
access examined in the Hash Tables lesson of the Data Structures course.

An object produced with `Object.create(null)` has no chain at all. Such an object behaves
as a pure dictionary: because inherited names like `toString` are not present, the risk
of externally supplied keys colliding with built-in names is eliminated. In mappings
whose keys come from outside the program, this setup also makes `for...in` and `in`
results predictable. The language's real counterpart for this same need is the `Map`
type, taken up in the Advanced Collections topic.

## Summary

- Every object carries, in addition to its own properties, a link to a prototype object;
  the link is read with `Object.getPrototypeOf` and can be tested directly.
- Property lookup starts from the object itself, moves up the chain if not found, and
  ends in `undefined` at the `null` stop.
- Writing does not climb the chain; it always writes to the object itself and shadows a
  prototype property of the same name.
- `in` looks at the whole chain; `Object.hasOwn` and `Object.keys` look only at own
  properties; `JSON.stringify` also works with own properties only.
- The chain built by object literals reaches `null` through `Object.prototype`;
  `Object.create(null)` produces a prototype-less object that behaves as a pure
  dictionary.

## Next Step

In this lesson the prototype was set up by hand: a shared behavior object was written
first, then each record was bound to it with `Object.create`. This pattern works, but it
does not resemble a type definition — deriving a new record type, filling in shared
fields, and sharing behavior between two record types all require a repeating setup. The
next lesson examines the built-in patterns for object-to-object inheritance and shows how
the `new` operator sets up this link in your place.
