Prototypes
Co-created the Self programming language with Randall Smith.
function Person() {
}
const p = new Person()
Object.getPrototypeOf(p)
Object.getPrototypeOf(Person)
Object.getPrototypeOf(Object)
Object.getPrototypeOf(Function.prototype)
Object.getPrototypeOf(Object.prototype)
Object.getPrototypeOf(Function)
*There is a new feature in the latest JS version where properties can be private.
What is a Prototype?
Every object is born referencing to a prototype object(parent) by a secret property [[Prototype]] or __proto__.
There are two prototypes in JS
Even when you don't specify the prototype, a default prototype is set for every object.
const obj = {};
console.log(obj.toString)
// [Function: toString]
A prototype is a working object instance.
The prototype relationship between two objects is about inheritance.
An object specifies its prototype via the internal property [[Prototype]]
Every object has this property, but it can be null.
The chain of objects connected by the [[Prototype]] property is called the prototype chain.
(Rough) Algorithm —
Goal: Read middleName from the object obj. (Note that middleName does not exist in obj )
Create, Update, Delete a property
Object.getPrototypeOf
const prototype1 = {};
const object1 = Object.create(prototype1);
Object.getPrototypeOf(object1) === prototype1;
// expected output: true
Reflect.getPrototypeOf(target)
This method is almost the same method as Object.getPrototypeOf
__proto__
Object#isPrototypeOf()
parent.isPrototypeOf(obj)
obj.__proto__
Object.setPrototypeOf()
Object.setPrototypeOf(obj, prototype)
The Object.setPrototypeOf() method sets the prototype (i.e., the internal [[Prototype]] property) of a specified object to another object or null.
__proto__
const shape = {};
const circle = {}
// Set the object prototype.
// DEPRECATED. This is for example purposes only.
// DO NOT DO THIS in real code.
shape.__proto__ = circle;
var proto = {
describe: function () {
return 'name: '+this.name;
}
};
var obj = Object.create(proto);
obj.name = 'obj';
obj.describe();
Whenever you access a property via obj, JavaScript starts the search for it in that object and continues with its prototype, the prototype’s prototype, and so on.
a property in an object overrides a property with the same key in a “later” object: the former property is found first.
Object.create
Constructor Functions
When a function is used as a constructor (with the new keyword), its this is bound to the new object being constructed.
function C() {
this.a = 37;
}
var o = new C();
console.log(o.a); // 37
function C2() {
this.a = 37;
return {a: 38};
}
o = new C2();
console.log(o.a); // 38
When a function is executed with the new keyword, it does the following steps:
function Person(name, age) {
this.age = age;
this.name = name;
}
function Person(name, age) {
// this = {}
this.age = age;
this.name = name;
}
function Person(name, age) {
// this = {}
// Object.setPrototypeOf(this, Person.prototype)
this.age = age;
this.name = name;
}
function Person(name, age) {
// this = {}
// Object.setPrototypeOf(this, Person.prototype)
this.age = age;
this.name = name;
/*
let userHasWrittenReturn = false;
if (userHasWrittenReturn) {
let returnValue be user's return value;
if (returnValue is object) {
return returnValue;
}
return this;
}
return this;
*/
}
function Person(name, age) {
this.name = name;
this.age = age;
}
const p1 = new Person('A', 25);
const p2 = new Person('B', 32);
A constructor function should be called with new
new.target (pseudo property)
use strict
instanceof
if (!(this instanceof Person)) {
throw new Error('should be called with new');
}
The instanceof operator tests to see if the prototype property of a constructor appears anywhere in the prototype chain of an object.
Use ES6 classes
Psuedoclassical Inheritance
A pattern that uses a constructor function and the new operator, combined with a prototype added to the constructor is said to be Pseudoclassical.
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
function Rectangle(height, width) {
this.height = height;
this.width = width;
}
Distinguishing .prototype and [[Prototype]]
Prototypal Inheritance
function makePerson(name, age) {
return {
name,
age
}
}
const p1 = makePerson('A', 25);
const p2 = makePerson('B', 32);
Prototypal Inheritance
function makePerson(name, age) {
return {
name,
age,
nameInUpper() {
return this.name.toUpperCase();
},
};
}
const p1 = makePerson('a', 25);
const p2 = makePerson('b', 32);
p1.nameInUpper();
Prototypal Inheritance
function nameInUpper() {
return this.name.toUpperCase();
}
function makePerson(name, age) {
return {
name,
age,
};
}
const p1 = makePerson('a', 25);
const p2 = makePerson('b', 32);
console.log(nameInUpper.call(p1));
Prototypal Inheritance
const PersonSharedMethods = {
nameInUpper() {
return this.name.toUpperCase();
},
};
function makePerson(name, age) {
return {
name,
age,
nameInUpper: PersonSharedMethods.nameInUpper,
};
}
const p1 = makePerson('a', 25);
const p2 = makePerson('b', 32);
p1.nameInUpper();
Prototypal Inheritance
function makePerson(name, age) {
const newPerson = Object.create(makePerson.sharedMethods);
newPerson.name = name;
newPerson.age = age;
return newPerson;
}
makePerson.sharedMethods = {
nameInUpper() {
return this.name.toUpperCase();
},
};
const p1 = makePerson('a', 25);
const p2 = makePerson('b', 32);
console.log(p1.nameInUpper());
Prototypal Inheritance
function makePerson(name, age) {
const newPerson = Object.create(makePerson.prototype);
newPerson.name = name;
newPerson.age = age;
return newPerson;
}
makePerson.prototype = {
nameInUpper() {
return this.name.toUpperCase();
},
};
const p1 = makePerson('a', 25);
const p2 = makePerson('b', 32);
console.log(p1.nameInUpper());
function Person(name, age) {
this.name = name;
this.age = age;
}
const p1 = new Person('A', 25);
const p2 = new Person('B', 32);
Two important rules
An object (by default) always inherits from it's creator function (or class ) `.prototype` object.
Object.prototype's prototype is null.
1
2
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.ageInBinary = function() {
return this.age.toString(2);
}
const p1 = new Person('A', 25);
const p2 = new Person('B', 32);
function Person() {
}
const p = new Person()
Object.getPrototypeOf(p)
Object.getPrototypeOf(Person)
Object.getPrototypeOf(Object)
Object.getPrototypeOf(Function.prototype)
Object.getPrototypeOf(Object.prototype)
Object.getPrototypeOf(Function)
Person.prototype
Function.prototype
Function.prototype
Object.prototype
null
Function.prototype
In prototype-based languages, inheritance is the mechanism of basing an object or class upon another object.
function Person(name) {
this.name = name;
}
Person.prototype.printName = function () {
console.log(this.name);
};
function Employee(name, id) {
Person.call(this, name); //! acting as super() call
this.id = id;
}
Object.setPrototypeOf(Employee.prototype, Person.prototype);
Employee.prototype.printId = function () {
console.log(this.id);
};
const e = new Employee('arfat', 42);
e.printId();
e.printName();
ES6 classes are not something that is radically new: They mainly provide more convenient syntax to create old-school constructor functions.
class Person {
constructor(name) {
this.name = name;
}
toString() {
return `Person named ${this.name}`;
}
static logNames(persons) {
for (const person of persons) {
console.log(person.name);
}
}
}
class Employee extends Person {
constructor(name, title) {
super(name);
this.title = title;
}
toString() {
return `${super.toString()} (${this.title})`;
}
}
const jane = new Employee('Jane', 'CTO');
console.log(jane.toString());
No separator (, or ;)
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return `(${this.x}, ${this.y})`;
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // (A)
this.color = color;
}
toString() {
return super.toString() + ' in ' + this.color; // (B)
}
}
> Object.getPrototypeOf(ColorPoint) === Point
true
class Foo {}
class Bar extends Foo {
constructor(num) {
const tmp = num * 2; // OK
this.num = num; // ReferenceError
super();
this.num = num; // OK
}
}
class Foo {
constructor() {
return Object.create(null);
}
}
console.log(new Foo() instanceof Foo); // false