Prototypes

October 13, 2021

Useful links:

Table of contents

  1. Beginning
  2. getPrototypeOf / setPrototypeOf
  3. __proto__
  4. Object.create
  5. Prototype chains
  6. Function prototypes
  7. Prototype inheritance
  8. Global Objects
  9. ES6 Classes
  10. Extensions
  11. Quiz

Beginning

const a = {};

What properties does this object have? None? Let’s try to log it in browser. Screenshot 2021-10-13 at 14 56 18
Object a has [[Prototype]] . What is it? It’s just a hidden property. Value of this property is link to an object. In our case value of [[Prototype]] is link to Object.prototype. What is Object.prototype? Screenshot 2021-10-13 at 14 58 17
Just an object that’s have methods like toString() and etc (check photo). What does this connection between object a and Object.prototype mean?

const a = {};
console.log(a.toString());

Will there be a mistake? No. Why? JS engine like Okay I should execute toString in a. Let’s check a. a doesn’t have field toString, so let’s go to prototype of a object. It’s link to Object.prototype. Okay, I found toString in Object.prototype, let’s execute it. Engine checks all prototype chain. If object doesn’t have some field, engine go to prototype. If prototype doesn’t have filed - engine will check prototype of prototype until it finds this field or the prototype will be null. Prototype of Object.prototype is null. Don’t believe me? How to see prototype of something via JS but not dev tools?

getPrototypeOf / setPrototypeOf

These methods appeared in ES5. getPrototypeOf(obj) returns prototype of obj. setPrototypeOf(obj, anotherObj), now prototype of obj is anotherObj

const a = {
	print: () => console.log("Hello")
}

const b = {};

console.log(Object.getPrototypeOf(a) === Object.getPrototypeOf(b));
Object.setPrototypeOf(b, a);
console.log(Object.getPrototypeOf(a) === Object.getPrototypeOf(b));
b.print();
Show output true
false
Hello

__proto__

This property appeared in ES6. Use it instead of getPrototypeOf/setPrototypeOf.

const a = {
	print: () => console.log("Hello")
}

const b = {};

console.log(a.__proto__ === b.__proto__);
b.__proto__ = a;
console.log(a.__proto__ === b.__proto__);
b.print();
Show output true
false
Hello

Object.create

Object.create(...); creates a new object associated with the object we specified. const B = Object.create(A); - prototype of B is A.

Prototype chains

Thanks to prototype, we can delegate behaviour to someone with whom we are connected by a prototype

const Parent = {
  name: "Parent",
  doSomething: function() {
    console.log("Method from Parent, but name is taken from " + this.name)
  }
}

const ChildOfParent = Object.create(Parent);
ChildOfParent.name = "Child of Parent";

const ChildOfChild = Object.create(ChildOfParent);
ChildOfChild.name = "Child of Child";

ChildOfChild.doSomething();
Show output Method from Parent, but name is taken from Child of Child

Function prototypes

By default, all functions have a public, non-enumerable property called prototype.

function Foo() {
  // ...
}

Foo.prototype; // { }

Not a very good name. The prototype field (Foo.prototype) that appears in the function and the prototype of the function itself (Foo.__proto__) are different things

function Foo() {
  // ...
}

console.log(Foo.prototype === Foo.__proto__); // False!!!!!

Each object that is constructed via new FunctionName() syntax is given a link to the function prototype (FunctionName.prototype) by the its prototype (constructedObject.__proto__)

function Person() {}

const person = new Person();

console.log(person.__proto__ === Person.prototype);

Person.prototype also has a constructor filed references the function itself

We want the person object to have a name

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

const person = new Person("Ksenia");

console.log(person);
console.log(Person.prototype);
Each object will have a `name` field inside itself.

But we want each instance of Person to have a greeting method

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

Person.prototype.sayHello = function() {
	console.log("Hello, my name is " + this.name);
}

const person = new Person("Ksenia");

console.log(person);
console.log(person.sayHello());
console.log(Person.prototype);

Prototype inheritance

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

Person.prototype.mainInfo = function() {
	console.log(`Main info: ${this.name}, ${this.age}`);
}

function Student(name, age, department) {
	Person.call(this, name, age);
	this.department = department;
}

Student.prototype = Object.create(Person.prototype);
// There is no constructor property in Student. you need
// to create it yourself
// Student.prototype.constructor = Student;

Student.prototype.educationInfo = function() {
	console.log(`Education info: ${this. department}`);
}

const p = new Person("Ksenia", 19);
const s = new Student("Ksenia", 19, "Languages");

p.educationInfo() // error
s.educationInfo() // ok

Global objects

There are built-in objects in js (Object, Array, RegExp, Number, etc.)

const a = []; // Equal to const a = new Array();

When we create an instance of such “classes”, the __proto__ object refers to Class.prototype. And each Class.prototype refers to Object.prototype

const a = [];
console.log(a.__proto__ === Array.prototype); // true
console.log(a.__proto__.__proto__ === Object.prototype); // true

const b = 43;
console.log(b.__proto__ === Number.prototype); // true
console.log(b.__proto__.__proto__ === Object.prototype); // true

If you can’t understand why primitive have __proto__ check this

ES6 Classes

Just syntactic sugar for prototypes. MDN

Extensions

  • Loop
const a = {};
a.__proto__ = a; // TypeError: Cyclic __proto__ value
  • Right constructor
function Foo() {
  /* .. */
}

Foo.prototype = {
  /* .. */
};

Object.defineProperty(Foo.prototype, "constructor", {
  enumerable: false,
  writable: true,
  configurable: true,
  value: Foo,
});

Quiz

  1. True or false
const a = {};
const b = {};

console.log(a.__proto__ === b.__proto__);
Show answer True
  1. True or false
const a = {};
const b = [];

console.log(a.__proto__ === b.__proto__);
Show answer False
  1. True or false
const a = {};
const b = [];

console.log(a.__proto__ === b.__proto__.__proto__);
Show answer True
  1. True or false
class A {};
function B() {};

console.log(A.__proto__ === B.__proto__);
Show answer True
  1. True or false
function Kek() {
	this.hello = function() {
		console.log("hello");
  }
}

const a = new Kek();
const b = new Kek();

console.log(a.hello === b.hello);
Show answer False
  1. True or false
function Kek() {}

Kek.prototype.hello = function() {
	console.log("Hello");
}

const a = new Kek();
const b = new Kek();

console.log(a.hello === b.hello);
Show answer True
  1. True or false
function Kek() {
	this.hello = function() {
		console.log("hello");
  }
}

Kek.prototype.hello = function() {
	console.log("Hello");
}

const a = new Kek();
const b = new Kek();

console.log(a.hello === b.hello);
Show answer False
  1. Console output
Object.prototype.toString = function() { console.log("KEK") }
const a = {};
a.toString();
Show answer KEK
  1. Console output
Object.prototype.toString = function() { console.log("KEK") }
const a = {
	toString: () => console.log("LOL"),
};
a.toString();
Show answer LOL
  1. Console output
Object.prototype.toString = () => console.log("KEK");
const a = [1, 2, 3];
console.log(a.toString());
Show answer 1, 2, 3
  1. Console output
Array.prototype.toString = () => console.log("KEK");
const a = [1, 2, 3];
a.toString();
Show answer KEK