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
constructorFn
constructor 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
this
value 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>