Prototype and prototype chain with deep understanding of JavaScript

Write in front

Prototypes and prototype chains have always been important concepts in JavaScript. Understanding them helps us understand the relationship between predefined reference types and the implementation mechanism of object inheritance in JavaScript. The following is my understanding and summary of prototypes and prototype chains.

prototype

Prototype object understanding

The prototype property of the function object

Every function we create has a prototype attribute, which is a pointer to an object. The purpose of this object is to contain properties and methods that can be shared by all instances of a specific type. In short, the __proto__ properties of all objects instantiated by the function point to this object, which is the prototype of all instantiated objects of the function .

function Person(){
}
// 为原型对象添加方法Person.prototype.sayName = function(){ alert(this.name);}

Let us look at the relationship between them.

constructor property

When the function is created and the prototype property points to a prototype object, by default, the prototype object will get a constructor property. This property is a pointer to the function object where the prototype is located.

Taking the previous example, Person.prototype.constructor points to the Person function object.

Let's update the relationship diagram between them.

Object's __proto__ attribute

When we call the constructor to create a new instance, it will contain a pointer inside the instance that points to the prototype object of the constructor. This pointer is called [[Prototype]] in the fifth edition of ECMA-262. All objects contain the [[Prototype]] attribute, pointing to their prototype object, which is a key concept in understanding the prototype chain.

It should be noted that there is no standard way to access the [[Prototype]] property in the script, but each object in Firefox, Safari and Chrome supports a property __proto__ to access, in order to distinguish the prototype property, we use below __proto__ to express.

According to the previous Person constructor, we create a new instance

var student = new Person();

console.log(student.__proto__ === Person.prototype); // true

As we can see from the above, this connection exists between the instance and the prototype object of the constructor, not between the instance and the constructor.

Let ’s take a look at the relationship between these objects

isPrototypeOf()

Although we have no way to access the [[Prototype]] property in the script, we can use the isPrototypeOf method to determine whether this relationship exists between objects.

The isPrototypeOf () method is used to test whether an object exists on the prototype chain of another object.

console.log(Person.prototype.isPrototypeOf(student)); // true

Object.getPrototypeOf()

A new method called Object.getPrototypeOf () has been added in ECMAScript 5, this method can return the value of [[Prototype]] as follows

console.log(Object.getPrototypeOf(student) === Person.prototype); // true

Prototype attribute

Attribute access

Whenever the code reads an attribute of an object, it will first search for the attribute in the object itself, and if the attribute is found, return the value of the attribute, if it is not found, continue to search for the prototype object corresponding to the object, and so on .

Because of this search process, if we add an attribute to the instance, this attribute will shield the attribute with the same name saved in the prototype object, because after searching for the attribute in the instance, we will not search backward.

Attribute judgment

Since an attribute may be either the instance itself or its prototype object, how do we judge it?

In the case where the property confirms the existence, we can use the hasOwnProperty () method to determine whether a property exists in the instance or in the prototype. Note that this method only returns true if the given attribute exists in the instance.

function Person() {};
Person.prototype.name = "laker" ;
var student = new Person();
console.log(student.name); // lakerconsole.log(student.hasOwnProperty("name")); // false

student.name = "xiaoming";console.log(student.name); //xiaoming 屏蔽了原型对象中的 name 属性console.log(student.hasOwnProperty("name")); // true

The above is mainly for the case where the attribute is confirmed to exist. If this attribute does not necessarily exist, the judgment is not accurate enough. Therefore, we need to first determine whether the attribute exists, and then perform the above judgment operation.

To determine whether an attribute exists, we can use the in operator, which returns true when the object can access a given attribute, regardless of whether the attribute exists in the instance or prototype.

Therefore, we can encapsulate such a function to determine whether a property exists in the prototype.

function hasPrototypeProperty(object, name){ return !object.hasOwnProperty(name) && (name in object);}

for-in loop

When using a for-in loop, all the enumerable attributes that can be accessed through the object are returned, including the attributes that exist in the instance and the attributes that exist in the prototype.

One thing to note is that instance attributes that mask non-enumerable attributes in the instance will also be returned in a for-in loop.

Get all attributes

If you want to get all enumerable instance properties on an object, you can use the Object.keys () method. This method receives an object as a parameter and returns a string array containing all enumerable properties.

If you want to get all the instance properties, whether it can be enumerated or not, we can use the Object.getOwnPropertyNames () method.

Prototype chain

Understanding the prototype chain

The concept of prototype chain is described in ECMAScript, and prototype chain is used as the main method to achieve inheritance. The basic idea is to use one reference type to inherit the properties and methods of another reference type.

The main implementation method of the prototype chain is to make the prototype object of the constructor equal to an instance of another type. Since the prototype object at this time is an instance, it will contain a pointer to another prototype, and accordingly another prototype also contains a Pointer to another constructor. If another prototype is an instance of another type, then the above relationship is still established, so that layers of progressiveness form a chain of instances and types. This is the basic concept of the prototype chain.

Let us look at an example.

function Super(){};function Middle(){};function Sub(){};Middle.prototype = new Super();Sub.prototype = new Middle();var suber = new Sub();

Let's take a look at the relationship between these objects

Default prototype

In fact, our prototype chain above is incomplete. Remember how we said that all reference types inherit Object? This inheritance is achieved through the prototype chain. We must remember that the default prototype of all functions is an instance of Object, so the default prototype will contain an internal pointer to Object.Prototype. This is the fundamental reason why all custom types inherit the default methods such as toString () and valueOf ().

Then I update the prototype chain above us.

Object.prototype is the end of the prototype chain, we can try to print Object.prototype .__ proto__, we will find that it returns a null empty object, which means the end of the prototype chain.

Functions are also objects

In fact, the above prototype chain is still missing, why? Because we know that in JavaScript, functions are also objects, so functions should also have their prototype objects. Let's understand the relationship between them through an example.

Example code:

function Foo(who) {this.me = who;}
Foo.prototype.identify = function() {return "I am " + this.me;};
function Bar(who) {Foo.call( this, who );}
Bar.prototype = Object.create( Foo.prototype );
Bar.prototype.speak = function() {alert( "Hello, " + this.identify() + "." );};
var b1 = new Bar( "b1" );var b2 = new Bar( "b2" );
b1.speak();b2.speak();

Let's first look at the relationship diagram of the prototype chain included in the above example without the function prototype:

The following is the relationship diagram of the prototype chain after adding the function prototype.

Write at the end

After summarizing the knowledge of prototypes and prototype chains, I feel that the understanding of JavaScript language is more profound, and it also lays the foundation for understanding the creation and inheritance of objects later. In fact, to understand the prototype chain, it is easy to understand the behavior and implementation of some predefined types in JavaScript.

Published 117 original articles · 69 praises · 10,000+ views

Guess you like

Origin blog.csdn.net/zsd0819qwq/article/details/105378892