Prototypes page

Demystify .prototype and ._proto_! We’ll cover what the "new" and "instanceof" operators are actually doing behind the scenes, and what prototype-based inheritance looks like in memory.

Overview

  • Shared properties
  • Methods/properties on a prototype
  • Setting up the proto chain
  • Object.create
  • Exercise!

Video

Slides

Exercise 1: new Operator

The problem

Write the new operator as if it was implemented in JS as a function. For example, instead of calling new Person('name'), we will call NEW(Person,['name']) as follows:

function Person(name) {
    this.name = name;
}

Person.prototype.speak = function(){ console.log('Hello!') }

// var person = new Person('name')
const person = NEW( Person, ['name'] );

person.speak();

NEW(constructor, args) will take:

  • constructor - a constructor function.
  • args - an array of arguments.

To get started, click the button at the bottom of the following:

<div id="qunit"></div>
<link rel="stylesheet" href="//code.jquery.com/qunit/qunit-1.14.0.css">
<script src="//code.jquery.com/qunit/qunit-1.14.0.js"></script>
<script type="module">
function NEW(constructorFn, args){

}
</script>
<script>
QUnit.test('NEW works', function(){
    var Person = function(name){
        this.name = name;
    }

    Person.prototype.species = 'Homo Sapien';
    Person.prototype.speak = function(toWhom) {
        return 'Hello ' + toWhom + '. My name is ' + this.name + '.';
    }
    var person = NEW(Person, ['Alexis']);

    equal(person.name, 'Alexis', 'new function called');
    var greet = 'Hello Justin. My name is Alexis.';
    equal(person.speak('Justin'), greet, 'method on prototype called');
});
</script>

What you need to know

The new operator does three things:

  1. Creates a new object.
  2. Assigns the object’s __proto__ to the constructor function’s prototype.
  3. Calls the constructorFn constructor function with the object as this.

Other things to know:

The solution

<div id="qunit"></div>
<link rel="stylesheet" href="//code.jquery.com/qunit/qunit-1.14.0.css">
<script src="//code.jquery.com/qunit/qunit-1.14.0.js"></script>
<script type="module">
function NEW(constructorFn, args){

    var obj = {};
    Object.setPrototypeOf(obj, constructorFn.prototype);

    // OPTION 2: __proto__
    // var obj = {};
    // obj.__proto__ = constructorFn.prototype;

    // OPTION 3: Object.create
    // var obj = Object.create(constructorFn.prototype);

    constructorFn.apply(obj, args);
    return obj;
}
</script>
<script>
QUnit.test('NEW works', function(){
    var Person = function(name){
        this.name = name;
    }

    Person.prototype.species = 'Homo Sapien';
    Person.prototype.speak = function(toWhom) {
        return 'Hello ' + toWhom + '. My name is ' + this.name + '.';
    }
    var person = NEW(Person, ['Alexis']);

    equal(person.name, 'Alexis', 'new function called');
    var greet = 'Hello Justin. My name is Alexis.';
    equal(person.speak('Justin'), greet, 'method on prototype called');
});
</script>

Exercise 2: instanceof Operator

The problem

Write the instanceof operator as if it was implemented in JS. For example, instead of calling person instanceof Person, we will call it as INSTANCEOF(person, Person) as follows:

var Person = function(name) {
    this.name = name;
}

var person = new Person( 'Alexis' );
INSTANCEOF( person, Person ) //-> true

To get started, click the button at the bottom of the following:

<div id="qunit"></div>
<link rel="stylesheet" href="//code.jquery.com/qunit/qunit-1.14.0.css">
<script src="//code.jquery.com/qunit/qunit-1.14.0.js"></script>
<script type="module">
function INSTANCEOF(obj, constructorFn){

}
</script>
<script>
QUnit.test('INSTANCEOF works', function() {

    var Person = function(name){
        this.name = name;
    };

    Person.prototype.species = 'Homo Sapien';
    Person.prototype.speak = function(toWhom) {
        return 'Hello ' + toWhom + '. My name is ' + this.name + '.';
    };

    var person = new Person('Alexis');
    QUnit.ok(INSTANCEOF(person, Person), 'person is an instanceof Person');
    QUnit.ok(INSTANCEOF(person, Object), 'person is an instanceof Object');
    QUnit.equal(INSTANCEOF(person, Array), false, 'person is not an instanceof Array');

    QUnit.equal(
        INSTANCEOF(Person.prototype, Person),
        Person.prototype instanceof Person,
        "Person.prototype is not an instance of Person")
});
</script>

What you need to know

The instanceof operator returns true if the prototype property of a constructor appears anywhere in the prototype chain of an object.

A constructor function’s prototype is not an instance of the constructor function:

var Person = function(name) {
    this.name = name;
}

console.log( Person.prototype instanceof Person ) // Logs: false

The solution

<div id="qunit"></div>
<link rel="stylesheet" href="//code.jquery.com/qunit/qunit-1.14.0.css">
<script src="//code.jquery.com/qunit/qunit-1.14.0.js"></script>
<script type="module">
function INSTANCEOF(obj, constructorFn){
    if( obj.__proto__ === constructorFn.prototype ) {
        return true;
    }
    const proto = Object.getPrototypeOf(obj)
    if( proto ) {
        return INSTANCEOF(proto, constructorFn);
    }
    return false;
}
</script>
<script>
QUnit.test('INSTANCEOF works', function() {

    var Person = function(name){
        this.name = name;
    };

    Person.prototype.species = 'Homo Sapien';
    Person.prototype.speak = function(toWhom) {
        return 'Hello ' + toWhom + '. My name is ' + this.name + '.';
    };

    var person = new Person('Alexis');
    QUnit.ok(INSTANCEOF(person, Person), 'person is an instanceof Person');
    QUnit.ok(INSTANCEOF(person, Object), 'person is an instanceof Object');
    QUnit.equal(INSTANCEOF(person, Array), false, 'person is not an instanceof Array');

    QUnit.equal(
        INSTANCEOF(Person.prototype, Person),
        Person.prototype instanceof Person,
        "Person.prototype is not an instance of Person")
});
</script>