Javascript prototype inheritance _ javascript skills

Source: Internet
Author: User
Tags hasownproperty
Javascript prototype inheritance is a theme that has been ruined, but I have never fully understood this problem, today, I spent some time reading several prototype implementation inheritance methods in Javascript mode. The following describes them one by one, finally, based on my own understanding, I proposed a complete implementation of inheritance. In the real sense, Javascript is not an object-oriented language and does not provide traditional inheritance methods, but it provides a prototype inheritance method, use the prototype Attributes provided by Alibaba Cloud to implement inheritance.

Prototype and prototype chain

Before prototype inheritance, we should first talk about prototype and prototype chain. After all, this is the basis for prototype inheritance.
In Javascript, each function has a prototype attribute pointing to its own prototype, and the object created by this function also has a _ proto _ attribute pointing to this prototype, the prototype of the function is an Object, so this Object will also have a _ proto _ pointing to its own prototype, so that the prototype of the Object can be further penetrated layer by layer, forming a prototype chain. The following figure illustrates the relationship between prototype and prototype chain in Javascript.

Each Function is an object created by a Function. Therefore, each Function also has a prototype pointing to a Function. It should be noted that the _ proto _ attribute of each object is actually formed in the prototype chain, rather than the prototype attribute of the function, which is very important.

Prototype inheritance

Basic Mode

The Code is as follows:


Var Parent = function (){
This. name = 'parent ';
};
Parent. prototype. getName = function (){
Return this. name;
};
Parent. prototype. obj = {a: 1 };

Var Child = function (){
This. name = 'child ';
};
Child. prototype = new Parent ();

Var parent = new Parent ();
Var child = new Child ();

Console. log (parent. getName (); // parent
Console. log (child. getName (); // child

This is the simplest method to implement prototype inheritance. The object of the parent class is directly assigned to the prototype of the subclass constructor, in this way, the subclass object can access the attributes in the prototype of the parent class and the parent class constructor. The prototype inheritance diagram of this method is as follows:

This method has obvious advantages and is easy to implement without any special operations. It also has obvious disadvantages. If the subclass needs to perform the same initialization action as the parent class constructor, you must repeat the operations in the parent class in the subclass constructor:

The Code is as follows:


Var Parent = function (name ){
This. name = name | 'parent ';
};
Parent. prototype. getName = function (){
Return this. name;
};
Parent. prototype. obj = {a: 1 };

Var Child = function (name ){
This. name = name | 'child ';
};
Child. prototype = new Parent ();

Var parent = new Parent ('myparent ');
Var child = new Child ('mychild ');

Console. log (parent. getName (); // myParent
Console. log (child. getName (); // myChild

In this case, you only need to initialize the name attribute. This method is inconvenient if the initialization work is constantly increasing. Therefore, we have the following improvement method.

Borrow Constructor

The Code is as follows:


Var Parent = function (name ){
This. name = name | 'parent ';
};
Parent. prototype. getName = function (){
Return this. name;
};
Parent. prototype. obj = {a: 1 };

Var Child = function (name ){
Parent. apply (this, arguments );
};
Child. prototype = new Parent ();

Var parent = new Parent ('myparent ');
Var child = new Child ('mychild ');

Console. log (parent. getName (); // myParent
Console. log (child. getName (); // myChild

The above method uses apply to call the constructor of the parent class in the subclass constructor to perform the same initialization, no matter how much Initialization is done in the parent class, sub-classes can also perform the same initialization. However, there is still a problem with the above implementation. The parent class constructor is executed twice, one in the subclass constructor, and the other in the assignment subclass prototype, therefore, we also need to make an improvement:

The Code is as follows:


Var Parent = function (name ){
This. name = name | 'parent ';
};
Parent. prototype. getName = function (){
Return this. name;
};
Parent. prototype. obj = {a: 1 };

Var Child = function (name ){
Parent. apply (this, arguments );
};
Child. prototype = Parent. prototype;

Var parent = new Parent ('myparent ');
Var child = new Child ('mychild ');

Console. log (parent. getName (); // myParent
Console. log (child. getName (); // myChild

In this way, we only need to execute the constructor of the parent class once in the subclass constructor, and inherit the attributes in the parent class prototype, which is in line with the original intention of the prototype, we put the content to be reused in the prototype, and we only inherit the reusable content in the prototype. The prototype diagram of this method is as follows:

Temporary constructor Mode)

There is still a problem with the last improved version of the borrow constructor mode above. It directly assigns the prototype of the parent class to the prototype of the subclass, which causes a problem, if the prototype of the subclass is modified, the modification will also affect the prototype of the parent class and the parent class object. This is certainly not what everyone wants to see. To solve this problem, we have a temporary constructor mode.

The Code is as follows:


Var Parent = function (name ){
This. name = name | 'parent ';
};
Parent. prototype. getName = function (){
Return this. name;
};
Parent. prototype. obj = {a: 1 };

Var Child = function (name ){
Parent. apply (this, arguments );
};
Var F = new Function (){};
F. prototype = Parent. prototype;
Child. prototype = new F ();

Var parent = new Parent ('myparent ');
Var child = new Child ('mychild ');

Console. log (parent. getName (); // myParent
Console. log (child. getName (); // myChild

The prototype inheritance diagram of this method is as follows:

It is easy to see that by adding a temporary constructor F between the parent class prototype and the subclass prototype, the relationship between the subclass prototype and the parent class prototype is cut off, in this way, the parent class prototype is not affected when the subclass prototype is modified.

My methods

In Javascript mode, the Holy Grail mode is over, But no matter which method is above, there is an issue that is not easily discovered. You can see that I added an obj object literal attribute to the prototype attribute of 'parent', but it has never been used. Let's take a look at the following situation based on the Holy Cup Model:

The Code is as follows:


Var Parent = function (name ){
This. name = name | 'parent ';
};
Parent. prototype. getName = function (){
Return this. name;
};
Parent. prototype. obj = {a: 1 };

Var Child = function (name ){
Parent. apply (this, arguments );
};
Var F = new Function (){};
F. prototype = Parent. prototype;
Child. prototype = new F ();

Var parent = new Parent ('myparent ');
Var child = new Child ('mychild ');

Console. log (child. obj. a); // 1
Console. log (parent. obj. a); // 1
Child. obj. a = 2;
Console. log (child. obj. a); // 2
Console. log (parent. obj. a); // 2

In the above case, when I modify the child object obj. at the same time, obj. a will also be modified, which has the same problem as the shared prototype. This occurs because when accessing child. obj. a, we will keep finding the prototype of the parent class along the prototype chain of the prototype, find the obj attribute, and then find the object. a. Let's look at the following situation:

The Code is as follows:


Var Parent = function (name ){
This. name = name | 'parent ';
};
Parent. prototype. getName = function (){
Return this. name;
};
Parent. prototype. obj = {a: 1 };

Var Child = function (name ){
Parent. apply (this, arguments );
};
Var F = new Function (){};
F. prototype = Parent. prototype;
Child. prototype = new F ();

Var parent = new Parent ('myparent ');
Var child = new Child ('mychild ');

Console. log (child. obj. a); // 1
Console. log (parent. obj. a); // 1
Child. obj. a = 2;
Console. log (child. obj. a); // 2
Console. log (parent. obj. a); // 2

There is a key issue here. When an object accesses a property in the prototype, the property in the prototype is read-only for the object, that is, the child object can read the obj object, however, the obj object reference in the prototype cannot be modified. Therefore, when the child modifies the obj, it does not affect the obj in the prototype. It only adds an obj attribute to its own object, overwrite the obj attribute in the parent class prototype. When the child object modifies obj. a, it first reads the reference of obj in the prototype. At this time, child. obj and Parent. prototype. obj is directed to the same object, so child is directed to obj. a's modification will affect the Parent. prototype. obj. the value of a, which affects the object of the parent class. In AngularJS, the Inheritance Method of $ scope Nesting is implemented by prototype inheritance in the model Javasript.
According to the above description, as long as the prototype accessed by the subclass object is the same as that of the parent class, the above situation will occur, so we can copy the parent class prototype and assign it to the subclass prototype. In this way, when the subclass modifies the attributes in the prototype, it only modifies a copy of the parent class prototype, does not affect the parent class prototype. The specific implementation is as follows:

The Code is as follows:


Var deepClone = function (source, target ){
Source = source | {};
Var toStr = Object. prototype. toString,
ArrStr = '[object array]';
For (var I in source ){
If (source. hasOwnProperty (I )){
Var item = source [I];
If (typeof item = 'object '){
Target [I] = (toStr. apply (item). toLowerCase () = arrStr): []? {};
DeepClone (item, target [I]);
} Else {
DeepClone (item, target [I]);
}
}
}
Return target;
};
Var Parent = function (name ){
This. name = name | 'parent ';
};
Parent. prototype. getName = function (){
Return this. name;
};
Parent. prototype. obj = {a: '1 '};

Var Child = function (name ){
Parent. apply (this, arguments );
};
Child. prototype = deepClone (Parent. prototype );

Var child = new Child ('child ');
Var parent = new Parent ('parent ');

Console. log (child. obj. a); // 1
Console. log (parent. obj. a); // 1
Child. obj. a = '2 ';
Console. log (child. obj. a); // 2
Console. log (parent. obj. a); // 1


Based on all the above considerations, the specific implementation of Javascript inheritance is as follows. Here we only consider the situation where both Child and Parent are functions:

The Code is as follows:


Var deepClone = function (source, target ){
Source = source | {};
Var toStr = Object. prototype. toString,
ArrStr = '[object array]';
For (var I in source ){
If (source. hasOwnProperty (I )){
Var item = source [I];
If (typeof item = 'object '){
Target [I] = (toStr. apply (item). toLowerCase () = arrStr): []? {};
DeepClone (item, target [I]);
} Else {
DeepClone (item, target [I]);
}
}
}
Return target;
};

Var extend = function (Parent, Child ){
Child = Child | function (){};
If (Parent = undefined)
Return Child;
// Borrow the parent class Constructor
Child = function (){
Parent. apply (this, argument );
};
// Inherit the parent class prototype through deep copy
Child. prototype = deepClone (Parent. prototype );
// Reset the constructor attribute
Child. prototype. constructor = Child;
};

Summary

With so much said, in fact, the implementation of inheritance in Javascript is very flexible and diverse, and there is no best way, you need to implement different inheritance methods according to different needs, the most important thing is to understand the principle of implementation inheritance in Javascript, that is, the prototype and prototype chain. As long as you understand this, you can easily implement inheritance by yourself.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.