JavaScript Prototypes and Classes

Dr. Greg Bernstein

Updated April 25th, 2021

Prototypes and Classes

Learning Objectives

OOP = Object Oriented Programming

  • Understand the Object __prototype__ property and its chaining
  • Understand how this enables OOP inheritance
  • Understand and use JavaScript class declaration to create OOP classes
  • Understand extends declaration for use in OOP inheritance

Prototype References

class References

Get Hands on

  • Try all the examples as you read the following slides

  • It will really help things sink in

The __proto__ property

Note __proto__ is in the ES6 standard but it has been a de facto standard in many browsers for quite a while.

var myObjA = {stuff: "Think about stuff", number: 42};
var myObjB = {course: "CS3520", students: 34};
myObjB.__proto__ = myObjA;
console.log(myObjB); // Doesn't look different
console.log(myObjB.stuff); // How?

More __proto__

var myObjA = {stuff: "Think about stuff", number: 42};
var myObjB = {course: "CS3520", students: 34};
var myObjC = {music: "Metal Baroque", guitarist: "JS Bach"};
myObjB.__proto__ = myObjA;
myObjC.__proto__ = myObjB;
console.log(myObjC); // Doesn't look different
console.log(myObjC.number); // How?

The Prototype Chain 1

JavaScript looks for properties (including functions) on every object on the __proto__ chain until null is encountered.

console.log(myObjC)
console.log(myObjC.__proto__)
console.log(myObjC.__proto__.__proto__)
console.log(myObjC.__proto__.__proto__.__proto__)
console.log(myObjC.__proto__.__proto__.__proto__.__proto__)

The Prototype Chain 2

for (var prop in myObjC) {
    console.log(`${prop} has value ${myObjC[prop]}`);
}
console.log("Keys for myObjC:");
console.log(Object.keys(myObjC));

Enumerate Object Properties 1

From MDN

  • for...in loops: traverses all enumerable properties and its prototype chain

  • Object.keys(o): returns an array with all the own enumerable properties’ names (“keys”) of an object o.

Enumerate Object Properties 2

From MDN

  • Object.getOwnPropertyNames(o): returns an array containing all own properties’ names (enumerable or not) of an object o.

New and Updated Properties

From plain english

New / updated properties are assigned to the object, not to the prototype

Try it!

Let’s add a property to myObjC already in myObjB or myObjA

myObjC.number = 3;
myObjC.course = "Web Dev";
console.log(myObjC)
console.log(myObjC.__proto__)
console.log(myObjC.__proto__.__proto__)

Is this behavior useful?

  • Yes, allows sharing of functions and properties
  • Prevents accidental overwrites
  • Kind of hints at inheritance

Prototypical Inheritance 1

  • The JavaScript mechanisms:
    • __proto__
    • Constructor Functions with prototype and constructor properties (not covered)
  • Provide the basis for fairly rich inheritance modeling
    • All inheritance in JavaScript is based on these mechanisms

Prototypical Inheritance 2

  • The raw use of these mechanisms for inheritance hierarchies is somewhat error prone so ES6 introduced classes to help us out.

  • JavaScript classes do not add any new functionality, just much easier and more readable syntax to set up prototypical inheritance hierarchies and provide access to parent functions.

JavaScript Classes

Basic Syntax

See MDN class

class Board {
    constructor() { // all properties here
    this.year = 2010;
    this.make = "Mikes Lab";
    this.weight = "10lbs";
    this.style = "Formula";
    }

    about() { // nice simple method definition
        return `${this.year} ${this.make} ${this.style} board`;
    }

    sailRecommendation() { // nice simple method definition
        return `${this.style} sails`;
    }
}

Try the simple class

Much nicer syntax, same implementation

var b1 = new Board();
b1.about();
b1.sailRecommendation();
console.log(b1.__proto__)
console.log(b1.__proto__.constructor)
b1 instanceof Board

Key points

From Deep Dive into Classes

  • Classes can only contain method definitions, not data properties;

  • When defining methods, you use shorthand method definitions;

  • Unlike when creating object literals, you do not separate method definitions in class bodies with commas;

Extending (inheritance) 1

Start with a base class:

class Mammal {
    constructor(commonName) {
        this.backbone = true;
        this.neocortex = true;
        this.name = commonName;
    }

    locomotion() {
        return "usually walking, but not always";
    }

    speak() {
        return "Some kind of mammal sound";
    }
}

Extending (inheritance) 2

Extend from a base class using extend keyword:

class Marsupial extends Mammal {
    constructor(commonName) {
        super(commonName); // parent constructor
        this.pouch = true;
        this.aussie = "likely";
    }

    speak() {
        return "Some kind of Marsupial sound";
    }
}

Try using these classes

mam1 = new Mammal("Kitty");
mam1.locomotion();
mam1.speak();
mars1 = new Marsupial("Taz Devil");
mars1.speak();
mars1.locomotion();
Object.keys(mars1)
Array [ "backbone", "neocortex", "name", "pouch", "aussie" ]
Object.keys(mam1)

Look at Prototype Chain

Check if the prototype chain is setup:

console.log(mam1)
console.log(mam1.__proto__)
console.log(mam1.__proto__.constructor)
console.log(mars1)
console.log(mars1.__proto__)
console.log(mars1.__proto__.constructor)
console.log(mars1.__proto__.__proto__)
console.log(mars1.__proto__.__proto__.constructor)

Try InstanceOf

mam1 instanceof Mammal
mars1 instanceof Mammal
mars1 instanceof Marsupial
mam1 instanceof Marsupial

Key Points for Extend 1

Summarized from Deep Dive into Classes

  • Subclasses are declared with the class keyword, followed by an identifier, and then the extends keyword, followed by an identifier.

  • If your derived class needs to refer to the class it extends, it can do so with the super keyword.

Key Points for Extend 2

Summarized from Deep Dive into Classes

  • A derived class can’t contain an empty constructor. You must call super in the constructor of a derived class before you use this.

Super duper stuff

Summarized from Deep Dive into Classes

In JavaScript, there are precisely two use cases for the super keyword.

  • Within subclass constructor calls.

  • To refer to methods in the superclass. Within normal method definitions, derived classes can refer to methods on the parent class with dot notation: super.methodName.

// reveal.js plugins