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:
- Creates a new object.
- Assigns the object’s
__proto__to the constructor function’sprototype. - Calls the
constructorFnconstructor function with the object asthis.
Other things to know:
- You can use Object.setPrototypeOf(obj, prototype) to set the
__proto__property of an element. - Use apply to
call a function (also constructor functions) with a
thisvalue and an array of arguments.
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>