Talking about the technique of JavaScript prototype inheriting _javascript

Source: Internet
Author: User
Tags hasownproperty

In the real sense, JavaScript is not an object-oriented language, does not provide a traditional way of inheritance, but it provides a way of prototype inheritance, using the prototype properties provided by itself to achieve inheritance.

Prototype and prototype chain

Before inheriting the prototype, we must first talk about the prototype and the prototype chain, after all, this is the basis for implementing the prototype inheritance.
In JavaScript, each function has a prototype attribute prototype to its own prototype, and the object created by the function also has a __proto__ attribute pointing to the prototype, and the function's prototype is an object, so the object also has a __proto__ Point to its own prototype, so that it is layered into the object's prototype, thus forming the prototype chain. The following diagram is a good explanation of the relationship between the prototype and the prototype chain in JavaScript.

Each function is an object created by the function functions, so each function also has a __proto__ property that points to the function's prototype. It is important to note here that the __proto__ property of each object, rather than the prototype attribute of the function, is the real form of the prototype chain.

Prototype inheritance

Basic mode

Copy Code code 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 way to implement the prototype inheritance, directly assigning the object of the parent class to the prototype of the subclass constructor, such that the object of the class can access the properties in the parent class and the prototype of the parent class constructor. The prototype inheritance diagram for this method is as follows:

The advantages of this method are obvious, the implementation is very simple, do not need any special operations, and the disadvantage is obvious, if the subclass needs to do the same initialization action in the parent class constructor, then you have to repeat the action in the parent class in the subclass constructor:

Copy Code code 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

This situation is only required to initialize the Name property, which is inconvenient if the initialization work is increasing. So there is a way to improve it.

Borrowing constructors

Copy Code code 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 performs the same initialization in the subclass constructor by invoking the constructor of the parent class, so that subclasses can perform the same initialization work regardless of how much initialization is done in the parent class. But there is one problem with this implementation, which is that the parent constructor was executed two times, once in a subclass constructor, and once in the assignment subclass prototype, which is redundant, so we need to make an improvement:

Copy Code code 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

So we just need to execute the constructor of the parent class once in the subclass constructor. You can also inherit the attributes from the parent prototype, which is also in line with the original intent to put the reusable content in the prototype, and we just inherit the reusable content from the prototype. The prototype diagram of the above method is as follows:

Temporary constructor pattern (Holy Grail mode)

The last version to borrow the constructor pattern is still problematic, which assigns the prototype of the parent class directly to the subclass's prototype. This creates the problem that if the stereotype of the subclass is modified, the modification also affects the parent class's prototype, which in turn affects the parent object, which is certainly not what everyone wants to see. To solve this problem, there is a temporary constructor pattern.

Copy Code code 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 for this method is as follows:

It is easy to see that by adding a temporary constructor f between the parent prototype and the subclass prototype, the connection between the subclass prototype and the parent prototype is cut off, so that when the subclass prototype is modified it does not affect the parent class prototype.

My way.

"JavaScript mode" in the Holy Grail mode is over, but no matter which one of the above methods have a problem that is not easy to find. You can see I added an obj object literal attribute to the prototype attribute of ' Parent ', but it never worked. Let's look at the following situation on the basis of the Holy Grail model:

Copy Code code 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 this case, when I modify the child object Obj.a, the OBJ.A in the parent's prototype is also modified, which has the same problem as sharing the prototype. This happens because when we visit the CHILD.OBJ.A, we find the parent class prototype along the prototype chain, then we find the obj attribute, and then we modify the OBJ.A. Take another look at the following:

Copy Code code 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

Here's a key question, 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, but cannot modify the Obj object reference in the prototype, so when the child modifies obj, it does not affect obj in the prototype , it simply adds an obj attribute to its own object, overwriting the obj attribute in the parent class prototype. When the child object modifies OBJ.A, it first reads the reference to obj in the prototype, when Child.obj and Parent.prototype.obj point to the same object, so the modification of the child to OBJ.A affects the PARENT.PROTOTYPE.OBJ.A value. Which in turn affects the object of the parent class. The inheritance of $scope nesting in Angularjs is achieved by prototype inheritance in exemplary javasript.
According to the above description, as long as the prototype accessed in the subclass object is the same object as the parent class prototype, then this happens, so we can copy the parent prototype and then assign it to the subclass prototype, so that when the subclass modifies the attributes in the stereotype, it simply modifies a copy of the parent prototype. Does not affect the parent class prototype. Specifically implemented as follows:

Copy Code code 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);

The var child = The 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


In combination with all of the above considerations, the specific implementation of JavaScript inheritance is as follows, where only the child and parent are functions:

Copy Code code 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 to child;
To borrow a parent class constructor
Child = function () {
Parent.apply (this,argument);
} ;
Inheriting a parent class prototype through a deep copy
Child.prototype = Deepclone (Parent.prototype);
Resetting the constructor property
Child.prototype.constructor = child;
} ;

Summarize

So much, in fact, in JavaScript, implementation of inheritance is very flexible and diverse, and there is no best way to implement different ways of inheriting according to different requirements, the most important thing is to understand the JavaScript implementation of the principle of inheritance, that is, the prototype and prototype chain problems, As long as you understand these, you can easily achieve inheritance.

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.