Prototype and prototype chain of JavaScript

Posted by lhcpr on Sun, 30 Jan 2022 10:12:10 +0100

preface
JavaScript prototype and prototype chain have always been the focus and difficulty of interview, which is not so easy to understand.

text
Understanding several key points of prototype makes it easier to understand the concept of prototype:

1. All reference types (arrays, objects, functions) can freely extend attributes (except null);

2. All reference types have a "proto" attribute (an implicit prototype is an object);

3. All functions have a "prototype" attribute (an explicit prototype is an object);

4. The "proto" attribute of all reference types points to the "prototype" attribute of its constructor;

5. When accessing the attribute of an object, if the object itself does not have the attribute, it will look for it in its "proto" attribute (that is, its prototype).

Note: in JavaScript, everything is an object.

prototype

// Define a constructor
function Fn(name,age){
	this.name = name;
	this.age = age;
}
 
/*
As can be seen from point 3 above, all functions have a prototype attribute, which is an ordinary object
 From point 1 above, all objects can freely extend attributes (except null) through prototype
*/
Fn.prototype = {
	// There are other properties in the prototype object
	showName: function(){
		console.log("My name is " + this.name);
	},
	showAge: function(){
		console.log("I'm " + this.age + " years old");
	}
}
 
let obj = new Fn('leo',18)
/*
When accessing the property of an object, if the property does not exist in the object itself,
Then it will go to the prototype attribute of its constructor
*/
obj.showName();   // My name is leo
obj.showAge();   // I'm 18 years old

This is the prototype. It's easy to understand. So why use prototypes?

Imagine if we want to create multiple objects through Fn()

function Fn(name,age){
	this.name =name;
	this.age = age;
	this.showName = function(){
		console.log("My name is " + this.name);
	},
	this.showAge = function(){
		console.log("I'm " + this.age + " years old");
	}
}

Then every object we create will contain showName and showAge methods, which will occupy a lot of resources. If it is implemented through the prototype, you only need to assign a value to the prototype attribute in the constructor and write the extended method in FN In the prototype attribute, so that each object constructed through this constructor can use the showName and showAge methods in the prototype attribute, which saves a lot of resources.

Prototype chain
If you understand the prototype, the prototype chain will be well understood.

It can be seen from point 5 that when accessing the attribute of an object, if the object itself does not have this attribute, it will look for it in the "prototype" attribute (i.e. prototype) of its constructor. Because the "prototype" attribute is also an object, it also has a "_ proto_" attribute

// Define a constructor
function Foo(name,age){
    this.name = name;
    this.age = age;
}
Object.prototype.toString = function(){
	console.log("My name is " + this.name + " And I'm " + this.age + " years old");
}
let fn = new Foo('leo',18);
fn.toString();   // My name id leo And I'm 18 years old
 
console.log(fn.__proto__ === Foo.prototype);   // true
 
console.log(Foo.prototype.__proto__ === Object.prototype);   // true
 
console.log(fn.toString === Foo.prototype.__proto__.toString);   // true
 
// Prototype chain mechanism (looking up layer by layer), FN ToString actually accesses object prototype. toString
console.log(fn.toString === Object.prototype.toString);   // true
 
console.log(Object.prototype.__proto__ === null);   // true
 
 

From the above example, the following figure is obtained (image source network)


1. The constructor of fn instance is Foo(), so

fn.__ proto__ === Foo.prototype

2,Foo.prototype is also an object (with the proto attribute), and its constructor is Object(), so

Foo.prototype.__proto__ === Object.prototype

3. toString() is in object In prototype, when the method does not exist in the called object itself, it will look up layer by layer until it is null.
4. When fn calls toString(), it is found that fn itself does not have this method, so it goes to foo Find it in the prototype, find it or not, and then continue to object Find it in the prototype. If it is found, call object toString() method in prototype.
This is the prototype chain, fn which can call object The method in prototype is precisely because of the mechanism of prototype chain.

Note: when using a prototype, it is generally recommended to write the method to be extended in the prototype attribute of the constructor.

After understanding the above example, let's take a look at the following figure (image source network)

Combined with the above example for analysis

fn: custom objects

Foo: custom function

1. Custom function (new) - > custom object

let fn = new Foo('leo',18);

2. Custom object (proto) – > custom function prototype

fn.__ proto__ === Foo.prototype

3. In Js, everything is an object, and user-defined functions are no exception, that is, they also exist__ proto__ Property, and the constructor of the custom function is Function(), so

Foo.__proto__ === Function.prototype

4. The constructor of Object() is also Function(), so

Object.__proto__ === Function.prototype

5,Function.prototype is also an object, and there are__ proto__ Property, so get

Function.prototype.__proto__ === Object.prototype
 
// Function is also an object and has__ proto__ Property, which is also an object, also has__ proto__ attribute
Function.__proto__.__proto__ === Object.prototype

6,Object.prototype is also an object, and there are__ proto__ Property, the difference is

// Object.prototype is the top layer, and its__ proto__ null
Object.prototype.__proto__ === null   

Topics: Javascript Front-end