JavaScript Inheritance (prototype chain)

Source: Internet
Author: User

We know that inheritance is an integral part of the OO language, and for JavaScript as well. There are two types of inheritance: One, the interface inherits, only the signature of the method, and the other, implements the inheritance, inherits the actual method. JavaScript does not support signing, so only implementation inheritance. The implementation of inheritance is mainly dependent on the prototype chain. Here I will focus on the prototype chain as a few of the main ways that inheritance:

    • Prototype chain inheritance
    • Borrowing constructor inheritance
    • Combination Inheritance (emphasis)
The first part: prototype chain inheritance

A

To say the prototype chain inheritance, we have to first introduce the concept of prototype chain .

Imagine that if you make the prototype object equal to an instance of another object, the prototype object will now contain a pointer to another prototype. Accordingly, another prototype will also contain a pointer to another constructor. Assuming another prototype is another type of instance, then the above relationship is still established, so the level of progression, it constitutes a chain of examples and prototypes (note: Here the case and prototype are relative), this is the basic concept of the prototype chain.

  

123456789101112131415 function   supertype () {      this . property= true ; } supertype.prototype.getsupervalue= function () {       return   this . property; }; function   subtype () {      this . subproperty= false ; } subtype.prototype= new   supertype (); subtype.prototype.getsubvalue= function () {      return   this . Subproperty; } var   instance= new   subtype (); Console.log (Instance.getsupervalue ()); //true

In the above code, we can see that the prototype of subtype is an instance of supertype, so all the properties and methods that existed in the supertype instance now exist in Subtype.prototype. And instead of using the prototype object provided by the subtype default, we replaced it with a new prototype object (that is, an instance of supertype). Therefore, the new prototype object not only has all the properties and methods that are owned by an instance of Supertype, but also has a pointer inside it, pointing to the Supertype prototype. That is:instance points to subtype's prototype, and subtype's prototype points to Supertype's prototype. It is worth noting that the property isnow in Subtype.prototype ( because this in the Supertype constructor points to the created object instance ).

  When an instance property is accessed in read mode, the search process searches up the prototype chain. For example, calling Instance.getsupervalue () will go through three search steps: (1). If the method exists in the search instance, the result: none. (2). Along the prototype chain, search for the method in Subtype.prototype, the result: none. (3). Continue along the prototype chain, searching for the method in Supertype.prototype, the result: exists. Then stop searching. In other words: in the case of a property or method not found, the search process will always be a loop to the end of the prototype chain to stop.

Note: Instance.constructor now points to supertype, because subtype's prototype points to the prototype of another object--supertype, and the constructor attribute of the prototype object points to supertype. We can use the following code to make validation:

1 console.log(instance.constructor);

The final return is the Supertype constructor.

  Important: Don't forget the default prototype. We know that all reference types inherit object, and this inheritance is implemented through the prototype chain, where the default prototype for all functions is an instance of object, so the default prototype will contain an internal pointer to Object.prototype. This is also the root cause of all reference types that inherit the ToString (), ValueOf () method. We can use the following code to make validation:

123 console.log(Object.prototype.isPrototypeOf(instance));//trueconsole.log(SuperType.prototype.isPrototypeOf(instance));//trueconsole.log(SubType.prototype.isPrototypeOf(instance));//true

That is to say, the prototype object of Instace instance object is Object.prototype, Supertype.prototype, Subtype.prototype respectively. In addition, we can use the instanceof operator to judge the relationship between the instance instance and the constructor, as follows:

123 console.log(instance instanceofObject);//trueconsole.log(instance instanceof SuperType);//trueconsole.log(instance instanceofSubType);//true

That is, instance is an instance of the object Supertype subtype. Here we use a chart to show the relationship between them.

  Here, we can assume that the bold line is the prototype chain (the chain of instances and prototypes).

From this chart, we can see that subtype prototype has no Constructer property and no point to the subtype constructor, because the prototype object created at the same time as the subtype constructor is not the same as the prototype object. This prototype object is an instance of supertype. Notice that the last two prototype objects have a [[prototype]] property, because they are treated as instances.

B

Define the method with caution

  When subtypes sometimes need to be overwritten (as is the same as overriding attributes in the prototype), see "Understanding the evolution of object patterns in JavaScript (prototypes)") a method of a super type, or you need to add a method that does not exist in the superclass. At this point, it should be noted that the code to add a method to the prototype must be placed after replacing the prototype's statement (using the Superclass object instance as the prototype of the subtype). look at the following code:

12345678910111213141516171819     functionSuperType(){    this.property=true;}SuperType.prototype.getSuperValue=function(){    returnthis.property;};function SubType(){    this.subproperty=false;}SubType.prototype=newSuperType();//这一句代码即为替换的原型的语句SubType.prototype.getSubValue=function(){    returnthis.subproperty;//这时在子类型中新添加的方法}SubType.prototype.getSuperValue=function(){    return false;//这时在子类型添加的超类型的同名方法,用于覆盖超类型中的方法,因此,最后反悔了false}varinstance= newSubType();console.log(instance.getSuperValue());//false

  

if the order is reversed, then these two newly added methods are invalid, and eventually the result of Instance.getsupervalue () is still searched from the superclass and returns false. at this point because if reversed, then the method added to the subtype the first prototype, after replacing the prototype, you can only inherit the super-type, and the method just added will not be shared by the instance, when the instance of [[prototype] It points to the prototype object after the substitution and not to the original prototype object that added the method.

It is also important to note that when you implement inheritance through a prototype chain, you cannot use object literals to create a prototype method (this will create a prototype object again, not just the object replaced with the superclass instance), because it will cut off the prototype chain and cannot implement inheritance.

C

The problem of using the prototype chain alone

Issue 1: The main problem is when a prototype contains a reference type value. First, look back at the following prototype schema-creation methods, which are shared by all instances of the prototype properties that contain reference-type values, so that changing one of the instances will be changed, which is not what we want. This is why the value of a reference type is defined in the constructor instead of in the prototype object, as explained in the previous tutorial on prototypes. The same problem is true for the prototype chain.

Look at the following code;

12345678910     functionSuperType(){   this.colors=["red","blue","green"];}function SubType(){}SubType.prototype=new SuperType();//这时,SuperType中的this对象指向的是SubType.prototypevar instance1=new SubType();instance1.colors.push("black");console.log(instance1.colors);//["red", "blue", "green", "black"]var instance2=newSubType();console.log(instance2.colors);//["red", "blue", "green", "black"]

  

  The This in the Supertype constructor must point to the new object created by him, And Subtype.prototype is the new object, so subtype's prototype object has the colors attribute, because this property value is an array (reference type), so even though we meant to add a "black" to Instance1, But in the end it inevitably affected the instance2. There is a problem with colors in the constructor, and if you put it in another prototype object, there is still a problem. Therefore, this is a problem with the inheritance of the prototype chain.

  

  Question two:

  When you create an instance of a subtype, you cannot pass parameters to a super-type constructor. In fact, it should be said that there is no way to pass parameters to a super-type constructor without affecting all object instances.

We rarely use the prototype chain alone in practice because we use only the prototype chain to achieve the above two issues of inheritance.

Part II: Borrowing constructor inheritance

A

In order to solve these problems, people have invented the borrowing of constructors (also known as forged objects or classical inheritance), the core idea of this method is: call the Super-type constructor inside the sub-type constructor. because a function is simply an object that executes code in a particular environment, you can also execute a constructor on (future) newly created objects by using the Apply () and call () methods. Note: This type of inheritance does not use the knowledge of the prototype chain, and it has nothing to do with inheritance based on the prototype chain. the code is as follows:

1234567891011 function & nbsp supertype () {      this . colors=[ " Red " , " Blue " , " green " ]; } function   subtype () {      Supertype.call ( this ); //Call a superclass constructor inside a subtype constructor } var   instance1= new   subtype (); Instance1.colors.push ( "Black" ); Console.log (instance1.colors); //["Red", "Blue", "green", "black"] var   instance2= new   subtype (); Console.log (instance2.colors); //["Red", "Blue", "green"]

First, we can see that this kind of inheritance has done both the inheritance task and the effect we want to achieve: the modification of an attribute of one instance's value to a reference type does not affect the property value of the reference type of another instance.

It is important to note that this inheritance is completely different from the way the prototype chain is inherited. Look at the following code:

12 console.log(instance1 instanceofSubType);//trueconsole.log(instance1 instanceofSuperType);//false

Instance1 and Instance2 are not examples of supertype. The inheritance here is only superficial inheritance. We can analyze this inherited process: first declaring two constructors, then executing var instance1=new subtype (), that is, calling the constructor subtype with new, since the subtype constructor is called, At this point, the subtype execution environment is entered, and the supertype () function is called ( Note: New is not used here, so the supertype function should be treated as a general function ), and because subtype () The this is pointing to Instance1 (subtype is a constructor!). ), so the normal function supertype is called on the Instance1 object, because this common function is called on Instance1, so this in supertype points to Instance1, which is, The Instance1 object adds the colors attribute with the attribute value to the applied type, instance2 the same.

This solves the first problem in the inheritance of the prototype chain.

  

B

  with respect to the prototype chain, there is a great advantage to borrowing a constructor, that is, you can pass parameters to the superclass constructor in the subtype constructor. is as follows:

12345678910 functionSuperType(name){    this.name=name;}function SubType(){    SuperType.call(this,"zzw");    this.age=21;}var instance1=newSubType();console.log(instance1.name);//zzwconsole.log(instance1.age);//21

where Supertype.call (this, "ZZW"), and can write Supertype.apply (this,["ZZW"); (for this part of the knowledge point can be seen in the "JavaScript function of the Beauty ~" Part III).

So, let's start by analyzing how the function performs: first we declare two constructors, and then call the subtype constructor through the new operator, then go into the execution environment of the subtype constructor, execute the statement supertype.call (THIS.ZZW); The normal function is entered (again, as long as the new operator is not used, it is the general function) and the execution environment is passed, and the call method is used to indicate that the normal function supertype is called on the Instance1 object because it is called on the object. So the this in the Supertype function points to Instance1 and finally gets the Name property. After the execution of the code in the Supertype function execution environment is completed, the execution environment is returned to the subtype constructor, and the instance object obtains the age property with the property value 21.

Ok! The second problem of prototype chain inheritance is solved by borrowing constructor inheritance.

However, does borrowing a constructor have any drawbacks? The answer is YES! because only the borrowing constructor is used, the problem with the constructor pattern cannot be avoided-the method is defined in the constructor (which leads to waste). Furthermore, we say that this is different from the prototype chain, so the methods defined in the super-type prototype are not visible to the subtypes, and the result is that all types can only use the constructor pattern.

With this in mind, the techniques for borrowing constructors are rarely used alone.

Part III: Combinatorial Inheritance (pseudo-classical inheritance)

  As with creating an object, we combine the custom constructor pattern with the prototype pattern combination, which combines the prototype chain and the technology that borrows the constructor to take advantage of both. The main idea is to use the prototype chain to implement the inheritance of the prototype properties (that is, the attributes that you want to share for each instance ) and the method ( which is obviously inappropriate for borrowing the constructor, the inheritance method ), and by borrowing the constructor to implement the property of the instance ( That is, properties that you do not want to share, which are inherited by overriding the prototype property through instance properties . In this way, the function is reused by defining the method on the prototype ( that is, creating only one method at a time, being used multiple times, if the function is defined in the constructor, creating an instance, creating the same method, not reusing, affecting performance ), and ensuring that each instance has its own properties ( because borrowing constructors can pass arguments! The instance property is implemented by borrowing a constructor, which does not have to be overwritten .

  

Let's look at an example like this:

12345678910111213141516171819202122232425 functionSuperType(name,age){    this.name=name;//实例属性使用借用构造函数模式               this.age=age;//实例属性使用借用构造函数模式    this.colors=["red","blue","green"];//这个数组虽然会同时被原型链和借用构造函数添加使用,但最后根据原型链的搜索机制,是按照借用构造函数模式实现的。}SuperType.prototype.sayName=function(){    console.log(this.name);//实现同样效果的方法使用原型链模式};functionSubType(name,age){    SuperType.call(this,name,age);//借用构造函数模式的有点就是可以向子类型构造函数中的超类型构造函数传递参数,这里this的用法很重要    };SubType.prototype=newSuperType();//使用SuperType的实例来替换为SubType的原型对象SubType.prototype.constructor=SubType;// 这句代码即将SubType的原型对象的constructor属性指向SubType,但这一句去掉也不会影响结果。SubType.prototype.sayAge=function(){    console.log(this.age);//在原型对象中定义方法,可以使得该方法实现复用,增强性能};varinstance1=newSubType("zzw",21);instance1.colors.push("black");console.log(instance1.colors);//["red", "blue", "green", "black"]instance1.sayName();//zzwinstance1.sayAge();//21varinstance2=newSubType("ht",18);console.log(instance2.colors);//["red", "blue", "green"]instance2.sayName();//htinstance2.sayAge();//18

Key points: In the supertype constructor the code this.colors=["Red", "Blue", "green", and in fact will inherit from the individual prototype chain, adding the colors array to the Subtype prototype object, However, when the borrowing constructor executes, the colors array is added directly to the instance, so when the colors array is accessed, the colors array in the instance will not continue to search up the prototype chain once it has been searched, based on the search mechanism of the prototype chain (shielding effect). So the final change of the Instance1 colors does not affect the change of the colors array of instance2 (both colors arrays come from the instance itself rather than the prototype object).

A man who only imagines and does not act will never realize the joy of harvesting fruit. Just do it!

JavaScript Inheritance (prototype chain)

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.