Skip to main content

JavaScript Classes

JavaScript Prototype Chain

Written by Published

Every object has a prototype, and lookups walk up the chain until they find a match.

This is the machinery classes are built on.

Understanding it explains why inheritance behaves the way it does.

Example

Example

javascript

class Animal {
  speak() {
    return "sound";
  }
}

const dog = new Animal();

console.log(Object.getPrototypeOf(dog) === Animal.prototype);

The instance's prototype is the class's prototype object.

How Lookup Works

Reading a property checks the object itself first.

If it is not there, JavaScript checks the prototype, then its prototype, and so on.

The chain ends at null, and the result is undefined.

Syntax

Syntax

javascript

Object.getPrototypeOf(object);

Methods live on the prototype, not on the instance.

Methods Are Not on the Instance

This is why Object.keys never lists them.

Example

Example

javascript

class Person {
  constructor(name) {
    this.name = name;
  }
  greet() {
    return "hi";
  }
}

const ada = new Person("Ada");

console.log(Object.keys(ada).join(","));
console.log(typeof ada.greet);

Only name is an own property, but greet is still callable.

hasOwnProperty

This distinguishes an object's own properties from inherited ones.

Example

Example

javascript

class Person {
  constructor(name) {
    this.name = name;
  }
  greet() {
    return "hi";
  }
}

const ada = new Person("Ada");

console.log(ada.hasOwnProperty("name"));
console.log(ada.hasOwnProperty("greet"));

The output is true then false.

The Chain with Inheritance

Each extends adds another link.

Example

Example

javascript

class Animal {
  breathe() {
    return "breathing";
  }
}

class Dog extends Animal {
  bark() {
    return "woof";
  }
}

const rex = new Dog();

console.log(rex.bark() + " and " + rex.breathe());

bark was found on Dog and breathe one link further up.

Shadowing

An own property hides one further up the chain.

The prototype's version is untouched for everyone else.

Example

Example

javascript

class Animal {
  speak() {
    return "generic";
  }
}

const a = new Animal();
const b = new Animal();
a.speak = function () {
  return "just for a";
};

console.log(a.speak() + " / " + b.speak());

The output is just for a / generic.

The End of the Chain

Everything eventually reaches Object.prototype and then null.

Example

Example

javascript

const plain = {};

console.log(Object.getPrototypeOf(plain) === Object.prototype);
console.log(Object.getPrototypeOf(Object.prototype));

The output is true then null.

Complete Example

Complete Example

html

<!DOCTYPE html>
<html>
<head>
  <title>JavaScript Prototype Chain</title>
</head>
<body>

  <h1>The Prototype Chain</h1>

  <p id="out"></p>

  <script>
    class Animal {
      breathe() {
        return "breathing";
      }
    }

    class Dog extends Animal {
      bark() {
        return "woof";
      }
    }

    const rex = new Dog();

    const lines = [
      "own properties: " + JSON.stringify(Object.keys(rex)),
      "has bark of its own: " + rex.hasOwnProperty("bark"),
      "can bark: " + (typeof rex.bark === "function"),
      "is an Animal: " + (rex instanceof Animal)
    ];

    document.getElementById("out").innerHTML = lines.join("<br>");
  </script>

</body>
</html>

Try It Yourself

Run the above example in the Try It Editor.

Try adding an own method:

Assign rex.bark = function () {} and watch hasOwnProperty change.

Important Points

  • Property lookup walks up the prototype chain.
  • Methods live on the prototype, not the instance.
  • hasOwnProperty tells own from inherited.
  • An own property shadows one further up the chain.
  • The chain ends at null.

Conclusion

The prototype chain is what classes are made of.

It explains shared methods, inheritance and why Object.keys skips them.

Knowing it turns class behaviour from rules to remember into something obvious.