JavaScript basics-Object-oriented programming (II) inheritance

Source: Internet
Author: User
Tags shallow copy

Inheritance is one of the most talked about concepts in oo language. Many OO languages support two ways of inheriting: interface inheritance and implementation inheritance. Interface inheritance inherits only the method signature, while implementing inheritance inherits the actual method. As mentioned earlier, because the function is not signed, interface inheritance cannot be implemented in ECMAScript. ECMAScript only supports implementation of inheritance, and implementation of inheritance is mainly based on the prototype chain to achieve.

prototype chain

The concept of prototype chain is described in ECMAScript, and the prototype chain is used as the main method to implement inheritance. The basic idea is to use a prototype to let one reference type inherit the properties and methods of another reference type. Briefly review the following constructors. The relationship between the prototype and the instance: each constructor has a prototype object, and the prototype object is a pointer to the constructor, and the instance contains an internal pointer to the prototype object. So what happens if we let the prototype object be equal to another instance of the type? Obviously, the prototype object at this point will contain a pointer to another prototype, and a pointer to another constructor is also included in the other prototype. If another prototype is another type of example, then the relationship is still established, so that the level of progression, constitutes an example and prototype chain. This is the basic concept of the so-called prototype chain.

There is a basic pattern for implementing the prototype chain, which is roughly the following code:

    function supertype () {        this.property = true;    }    SuperType.prototype.getSuperValue = function () {        return this.property;    };    Function subtype () {        this.subproperty = false;    }    Inherited the supertype    subtype.prototype = new Supertype ();    SubType.prototype.getSubValue = function () {        return this.subproperty;    };    var instance = new Subtype ();    Alert (Instance.getsupervalue ());//true
The code above defines two types: Supertype and subtype. Each type has one property and one method. The main difference is that subtype inherits supertype, and inheritance is done by creating an instance of supertype and assigning that instance to Subtype.prototype. The essence of implementation is to rewrite the prototype object and replace it with an instance of a new type. In other words, all the properties and methods that existed in an instance of supertype now exist in Subtype.prototype. After we have established the inheritance relationship, we add a method to Subtype.prototype, which adds a new method based on inheriting the properties and methods of Supertype. The examples in this example, as well as the relationship between constructors and prototypes, are as follows:

In the above code, instead of using the prototype provided by the subtype default, a new prototype is replaced, and this new prototype is an example of supertype. As a result, the new prototype not only has all the properties and methods as an instance of Supertype, but also has a pointer inside it, pointing to the Supertype prototype. The end result is this: instance points to Subtype's prototype, and subtype's prototype points to Supertype's prototype. Getsupervalue (the 0 method is still in Supertype.prototype, but the property is in Subtype.prototype.) This is because the property is an instance attribute, and Getsupervalue () is a prototype method. Since Subtype.prototype is now an instance of Supertype, the property is of course in that instance. Also, be aware that Instance.constructor is now pointing to supertype, which is because the constructor in the original Subtype.prototype was rewritten.

By implementing the prototype chain, it essentially expands the prototype search mechanism described earlier. When an instance property is accessed in read mode, the attribute is first searched in the instance. If the property is not found, the prototype of the instance will continue to be searched. In the case of inheritance through the prototype chain, the search process continues upward along the prototype chain. Take the example above, call Instance.getsupervalue () will go through three search steps: 1) search for an instance ; 2) Search subtype.prototype;3) Search Supertype.prototype, the last to find the method, in the case of a property or method is not found, the search process will always be a loop to the end of the prototype chain to stop.

Don't forget the default prototype

In fact, the tactical prototype chain in the previous example is less than a link. We know that all reference types inherit object by default, and this inheritance is implemented through the prototype chain. As you can remember, the default prototype for all functions is an instance of object, so the default prototype contains an internal pointer to Object.prototype. This also formally all custom types inherit the root cause of the default methods, such as ToString (), ValueOf (). So, we say that the prototype shown in the example above should also include another hierarchy of inheritance. In a word, subtype inherits the Supertype, and Supertype inherits the object. When you call Instance.tostring (), you actually call the method that you saved in Object.prototype.

determining the relationship between prototypes and instances

There are two ways to determine the relationship between a prototype and an instance: The first is to use the instanceof operator, as long as the operator is used to test the constructor in the instance and the prototype chain, and the result returns TRUE. This is illustrated by the following lines of code:

    Alert (instance instanceof Object),//true    alert (instance instanceof supertype);//true    Alert (instance Instanceof subtype);//true
Because of the relationship of the prototype chain, we can say that instance is an instance of any of the types in object, Supertype, or subtype. Therefore, the result of testing these three constructors returns true.

The second way is to use the isPrototypeOf () method. Similarly, the isprototypeof () method also returns true as long as the prototype in the prototype chain is a prototype of the instance derived from the prototype chain.

define the method with caution

Subtypes sometimes need to rewrite a method in a superclass, or you need to add a method that does not exist in the superclass. However, the code to add a method to the prototype must be placed after the replacement of the prototype statement, see the following example:

    function supertype () {        this.property = true;    }    SuperType.prototype.getSuperValue = function () {        return this.property;    };    Function subtype () {        this.subproperty = false;    }    Inherited the supertype    subtype.prototype = new Supertype ();    Add New method    SubType.prototype.getSubValue = function () {        return this.subproperty;    };    Overriding a method in a superclass    SuperType.prototype.getSuperValue = function () {        return false;    };    var instance = new Subtype ();    Alert (Instance.getsupervalue ());//false
In the above code, after inheriting the new Method Getsubvalue () was added to the subtype, and then rewritten a method that already exists in the prototype chain, but overriding this method will block the original method. In other words, when calling Getsupervalue () from an instance of subtype, this redefined method is called, but when Getsupervalue () is called through an instance of Supertype, the original method continues to be called. It is important to note that these two methods must be defined after replacing the prototype with an instance of supertype.

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. Because this will rewrite the prototype chain.

problems with the prototype chain

Although the prototype chain is powerful, it can be used to implement inheritance, but it also has some problems. Among them, the most important problem comes from prototypes that contain reference types. The prototype properties that are described earlier that contain reference type values are shared by all instances, and that is why the properties are defined in the constructor, not in the prototype object. When you implement inheritance through a prototype, the prototype will actually become an instance of another type. As a result, the original instance attributes have naturally become the archetypal attributes of the present. Such as:

    function supertype () {        this.colors=["red", "Blue", "green"];    }    Function subtype () {}    //Inherits supertype    Subtype.prototype = new Supertype ();    var Instance1 = new subtype ();    Instance1.colors.push ("Black");    alert (instance1.colors);//"Red,blue,green,black"    var instance2 = new subtype ();    alert (instance2.colors);//"Red,blue,green,black"
The Supertype constructor In this example defines a colors property that contains an array (reference type value). Each instance of Supertype will have its own colors property that contains its own array. When a subtype is an instance, it also has its own colors property-just like a SubType.prototype.colors attribute created specifically. But what is the result? As a result, all instances of subtype share this colors property. This has been amply confirmed by the fact that our changes to instance1.colors can be reflected through instance2.colors.

The second problem with prototypes is that 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 should be no way to pass parameters to a super-type constructor without affecting all object instances. With this in mind, coupled with the problems that have just been discussed earlier in the prototype that contain reference type values, the prototype chain is rarely used alone.

borrowing Constructors

In the process of solving the problem of containing reference type values in the prototype, the developer begins to use a technique called borrowing a constructor (sometimes called a forged object or classic inheritance). The basic idea of this technique is quite simple, which is to call the superclass constructor inside the sub-type constructor. Don't forget that functions are simply objects that execute code in a particular environment, so you can also execute constructors on (future) newly created objects by using the Apply () and call () methods, such as:

    function supertype () {        this.colors=["red", "Blue", "green"];    }    Function subtype () {        //Inherits Supertype        Supertype.call (this);    }    Subtype.prototype = new Supertype ();    var Instance1 = new subtype ();    Instance1.colors.push ("Black");    alert (instance1.colors);//"Red,blue,green,black"    var instance2 = new subtype ();    alert (instance2.colors);//"Red,blue,green"
The super-type constructor was "seconded" in the code. By using the call () method (or the Apply () method), we are actually calling the Supertype constructor in the context of the newly created subtype instance (in the future). In this way, all the object initialization code defined in the Supertype () function is executed on the new subtype object. As a result, each instance of subtype will have its own copy of the Colors property.

Passing Parameters

With respect to the prototype chain, the borrowing constructor has a large, sometimes, parameter that can be passed to a superclass constructor in a subtype constructor, such as:

    function Supertype (name) {        this.name = name;    }    Function subtype () {        //inherits the Supertype, and also passes the parameter        supertype.call (this, "Nicholas");        Instance property        This.age =;    }    Subtype.prototype = new Supertype ();    var instance = new Subtype ();    alert (instance.name);//"Nicholas"    alert (instance.age);//29
The supertype in the above code only accepts a parameter name, which is assigned directly to a property. When the Supertype constructor is called inside the subtype constructor, the Name property is actually set for the instance of subtype. To ensure that the Supertype constructor does not override the properties of the subtype, you can add properties that should be defined in the subtype after the superclass constructor is called.

problems with borrowing constructors

If you just borrow a constructor, you won't be able to avoid the problem with the constructor pattern--methods are defined in the constructor, so the reuse of the function is not possible. Also, methods defined in a super-type prototype are not visible to the subtypes, and all types can only use the constructor pattern. With these problems in mind, the technique of borrowing constructors is seldom used alone.

Combining Inheritance

Combinatorial inheritance, sometimes called pseudo-classical inheritance, refers to the combination of the prototype chain and the technique of borrowing the constructor into a piece, thus exerting a succession pattern of the two's length. The idea behind it is to use the prototype chain to implement the inheritance of the prototype properties and methods, and to implement the inheritance of the instance properties by borrowing the constructor function. This allows the function to be reused both by defining the method on the prototype, and by ensuring that each instance has its own properties, such as:

 function        Supertype (name) {this.name = name;    This.colors = ["Red", "Blue", "green"];    } SuperType.prototype.sayName = function () {alert (this.name);    };        Function subtype (name, age) {//Inherits the Supertype, and also passes the parameter Supertype.call (this, name);    Instance attribute This.age = age;    } Subtype.prototype = new supertype ();    SubType.prototype.sayAge = function () {alert (this.age);    };    var Instance1 = new Subtype ("Nicholas", 29);    Instance1.colors.push ("Black"); alert (instance1.colors);//"Red,blue,green,black" instance1.sayname ();//"Nicholas" instance1.sayage ();//29 var ins    Tance2 = new Subtype ("Greg", 21); alert (instance2.colors);//"Red,blue,green" instance2.sayname ();//"Greg" instance2.sayage ();//21 
In this example, the Supertype constructor defines two properties: name and Colors. The Supertype prototype defines a method Sayname (). The subtype constructor passes the name parameter when it calls the Supertype constructor, and then it defines its own property, age. Then, the instance of Supertype is assigned to the prototype of subtype, and then the method Sayage () is defined on the new prototype. This allows two different subtype instances to have their own properties-including the colors attribute-and the same method.

Combined inheritance avoids the defects of prototype chains and borrowed constructors, and incorporates their advantages, known as the most common inheritance patterns in JavaScript. Furthermore, instanceof and isprototypeof () can also be used to identify objects that are created based on composite inheritance.

prototype Inheritance

Douglas Kest Rockford wrote an article in 2006 titled Prototypalinheritance in JavaScript (prototype inheritance in JavaScript). In this article, he describes a method of implementing inheritance that does not use a strictly constructed constructor. His idea was to use prototypes to create new objects based on existing objects without having to create custom types. To achieve this, he gives the following functions:

     function Object (o) {        function F () {}        f.prototype = O;        return new F ();    
Inside the object () function, a temporary constructor is created, then the incoming object is used as a prototype of the constructor, and a new instance of the temporary type is returned. Essentially, object () performs a shallow copy of the incoming object. Look at the following example:

     function Object (o) {        function F () {}        f.prototype = O;        return new F ();     var person = {             Name: "Nicholas",             friends:["Shelby", "Court", "Van"]     };     var Anotherperson = object (person);     Anotherperson.name = "Greg";     AnotherPerson.friends.push ("Rob");     var Yetanotherperson = object (person);     Yetanotherperson.name = "Linda";     YetAnotherPerson.friends.push ("Barbie");     alert (person.friends);//"Shelby,court,van,rob,barbie"
This archetypal inheritance, Rockford argues, requires that you have an object that can be used as the basis for another object. If you have such an object, you can pass it to the object () function, and then modify the resulting object according to your specific requirements. In this example, a person object can be used as the basis for another object, so we pass it to the object () function, and the function returns a new object. This new object takes the person as a prototype, so its prototype contains a primitive type value attribute and a Yetanotherperson share. In fact, this is equivalent to creating two copies of the person object.

ECMASCRIPT5 has standardized the prototype inheritance by adding a object.create () method. This method receives two parameters: an object that is used as a prototype for the new object and (optionally) an object that defines additional properties for the new object. In the case of passing in a parameter, the Object.create () behaves the same as the Object () method.

The second parameter of the Object.create () method has the same format as the second parameter of the Object.defineproperties () method: Each property is defined by its own descriptor. Any property specified in this manner overrides the same name property on the prototype object.

Parasitic Inheritance

Parasitic inheritance is a kind of train of thought which is closely related to prototype inheritance, and is also popularized by Gram Rockford. The idea of parasitic inheritance is similar to the parasitic constructor and factory pattern, which is to create a function that encapsulates the inheritance process, which in some way enhances the object, and finally returns the object as if it did all the work, such as:

     function Object (o) {        function F () {}        f.prototype = O;        return new F ();     function Createanother (original) {         var clone = object (original);         Clone.sayhi = function () {alert ("Hi");};         return clone;     }     var person = {             Name: "Nicholas",             friends:["Shelby", "Court", "Van"]     };     var Anotherperson = Createanother (person);     Anotherperson.sayhi ();//"Hi"

Parasitic combination pattern inheritance

As mentioned earlier, combinatorial inheritance is the most common inheritance pattern for JavaScript, but it also has its own shortcomings. The biggest problem with combinatorial inheritance is that, in any case, a two-time super-type constructor is called: one time when a subtype prototype is created, and the other is inside the subtype constructor. Yes, the subtype eventually contains all the instance properties of the superclass object, but we have to override those properties when we call the subtype constructor. The basic pattern of parasitic inherited inheritance is as follows:

    function Inheritprototype (subtype, supertype) {        var prototype = object (Supertype.prototype);        Prototype.constructor = subtype;        Subtype.prototype = prototype;    }

JavaScript basics-Object-oriented programming (II) 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.