Prototype inheritance of JavaScript

Source: Internet
Author: User

Prototype inheritance of JavaScript

JavaScript is an object-oriented language. In JavaScript, there is a classic saying that everything is an object. Since it is object-oriented, it has three main characteristics of object-oriented: encapsulation, inheritance, and polymorphism. Here we will talk about the inheritance of JavaScript, and the other two will talk about it later.

The inheritance of JavaScript is not the same as that of C ++. the inheritance of c ++ is based on classes, while that of JavaScript is based on prototypes.

Now the problem is coming.

What is the prototype? For the prototype, we can refer to the class in C ++ to save the attributes and methods of the object. For example, we can write a simple object.

The Code is as follows:

Function Animal (name ){

This. name = name;

}

Animal. prototype. setName = function (name ){

This. name = name;

}

Var animal = new Animal ("wangwang ");

We can see that this is an object Animal, which has a property name and a method setName. Note that once you modify prototype, such as adding a method, all instances of the object will share this method. For example

The Code is as follows:

Function Animal (name ){

This. name = name;

}

Var animal = new Animal ("wangwang ");

At this time, animal only has the name attribute. If we add a sentence,

The Code is as follows:

Animal. prototype. setName = function (name ){

This. name = name;

}

At this time, animal will also have the setName method.

Inherit this copy-from an empty object we know that there is an object in the basic JS type, and its most basic instance is an empty object, that is, the instance generated by the new Object () is called directly, or it is declared using the literal. Empty objects are "clean objects" and only pre-defined attributes and methods are available. All other objects inherit from empty objects, therefore, all objects have these predefined attributes and methods. The prototype is actually an object instance. Prototype means that if the constructor has A prototype object A, all instances created by the constructor must be copied from. Because the instance is copied from object A, the instance must inherit all attributes, methods, and other properties of object. So how is replication implemented? Method 1: Create and copy an instance from the prototype. The new instance and prototype occupy the same memory space. Although this makes obj1 and obj2 "completely consistent" with their prototype, it is also very economic-the consumption of memory space will increase rapidly.

Method 2: Copy at write time comes from the same spoofing System Technology: Copy at write time. A typical example of this spoofing is the dynamic link library (DDL) in the operating system. Its memory zone is always copied at write time.

We only need to specify in the system that obj1 and obj2 are equivalent to their prototype, so that when reading, we only need to follow the instructions to read the prototype. When you need to write the attributes of an object (such as obj2), we will copy a Prototype Image and direct subsequent operations to the image.

The advantage of this method is that we do not need a lot of memory overhead when creating instances and read attributes. We only use some code to allocate memory when writing for the first time, it also brings about some code and memory overhead. However, this overhead is no longer available since the efficiency of accessing images is the same as that of accessing prototypes. However, for systems that frequently perform write operations, this method is not more economical than the previous method. Method 3: Read traversal this method changes the replication granularity from prototype to member. This method is characterized by copying the member information to the instance image only when the member of an instance is written. When writing an object attribute, for example (obj2.value = 10), an attribute value named value is generated and placed in the member list of the obj2 object. Figure:

It can be found that obj2 is still a reference pointing to the prototype, and no object instance of the same size as the prototype is created during the operation. In this way, the write operation does not cause a large amount of memory allocation, so the memory usage is economical. The difference is that obj2 (and all object instances) needs to maintain a member list. This member list follows two rules: ensure that the member list is first accessed during read. If no attribute is specified in the object, the entire prototype chain of the object is traversed, until the prototype is null or the property is found. The prototype chain will be discussed later. Among the three methods, read traversal has the optimal performance. Therefore, the prototype inheritance of JavaScript is read traversal. After reading the code of the top object, constructor who is familiar with C ++ will certainly be confused. It is easy to understand without the class keyword. After all, there are function keywords, And the keywords are different. But what about Constructors? In fact, JavaScript also has a similar constructor, but it is called a constructor. When the new operator is used, the constructor has been called and this is bound as an object. For example, we use the following code:

The Code is as follows:

Var animal = Animal ("wangwang ");

Animal will be undefined. Some people may say that undefined is certainly not returned. Modify the definition of Animal objects:

The Code is as follows:

Function Animal (name ){

This. name = name;

Return this;

}

Guess what animal is now?

At this time, animal is changed to window. The difference is that the window is extended so that the window has the name attribute. This is because if this is not specified, it points to the window by default, that is, the top-level variable. The constructor can be called correctly only when the new keyword is called. So, how can we avoid the use of people missing the new Keyword? We can make some minor changes:

The Code is as follows:

Function Animal (name ){

If (! (This instanceof Animal )){

Return new Animal (name );

}

This. name = name;

}

In this way, nothing is lost. The constructor can also be used to indicate which object the instance belongs. We can use instanceof to judge, but instanceof returns true to both the ancestor object and the real object during inheritance, so it is not suitable. When you call new, constructor points to the current object by default.

The Code is as follows:

Console. log (Animal. prototype. constructor === Animal); // true

We can think differently: prototype has no value at all at the beginning of the function, and the implementation may be the following logic:

// Set__ proto _ is a built-in Member of the function. get_prototyoe () is its method.

The Code is as follows:

Var _ proto _ = null;

Function get_prototype (){

If (! _ Proto __){

_ Proto _ = new Object ();

_ Proto _. constructor = this;

}

Return _ proto __;

}

This avoids the creation of an object instance for every declared function and saves the cost. Constructor can be modified. The inheritance based on prototype is almost unknown to everyone.

There are several inheritance types of JS. Here we will talk about two types.

1. method 1 this method is the most commonly used and provides better security. We first define two objects

The Code is as follows:

Function Animal (name ){

This. name = name;

}

Function Dog (age ){

This. age = age;

}

Var dog = new Dog (2 );

It is easy to construct inheritance. It points the prototype of the sub-object to the instance of the parent object (note that it is an instance, not an object)

The Code is as follows:

Dog. prototype = new Animal ("wangwang ");

In this case, dog has two attributes: name and age. If the instanceof operator is used for dog

The Code is as follows:

Console. log (dog instanceof Animal); // true

Console. log (dog instanceof Dog); // false

In this way, inheritance is implemented, but there is a small problem.

The Code is as follows:

Console. log (Dog. prototype. constructor === Animal); // true

Console. log (Dog. prototype. constructor = Dog); // false

We can see that the object pointed to by the constructor has changed, which does not meet our purpose. We cannot determine who the new instance belongs. Therefore, we can add one sentence:

The Code is as follows:

Dog. prototype. constructor = Dog;

Let's take a look:

Copy the Code as follows:

Console. log (dog instanceof Animal); // false

Console. log (dog instanceof Dog); // true

Done. This method is part of the maintenance of the prototype chain, which will be described in detail below. 2. method 2 This method has its advantages and disadvantages, but its disadvantages are greater than its advantages. First look at the code

The Code is as follows:

Function Animal (name ){

This. name = name;

}

Animal. prototype. setName = function (name ){

This. name = name;

}

Function Dog (age ){

This. age = age;

}

Dog. prototype = Animal. prototype;

In this way, prototype copying is implemented.

The advantage of this method is that you do not need to instantiate an object (compared with method 1), saving resources. Constructor points to the parent object and can only copy the attributes and Methods declared by prototype for the parent object. That is to say, in the above Code, the name attribute of the Animal object cannot be copied, but the setName method can be copied. The most critical thing is that any modification to the prototype of the Child object will affect the prototype of the parent object, that is, the instances declared by the two objects will be affected. Therefore, this method is not recommended.

  Prototype chain

Anyone who has written about inheritance knows that inheritance can be inherited in multiple layers. In JS, this constitutes the prototype chain. The prototype chain has been mentioned many times. What is the prototype chain? An instance should have at least the proto attribute pointing to the prototype, which is the basis of the Object System in JavaScript. However, this attribute is invisible. We call it "Internal prototype chain ", so that the prototype of the constructor can be separated from the prototype of the constructor (also known as the prototype chain. We first construct a simple inheritance relationship based on the above Code:

The Code is as follows:

Function Animal (name ){

This. name = name;

}

Function Dog (age ){

This. age = age;

}

Var animal = new Animal ("wangwang ");

Dog. prototype = animal;

Var dog = new Dog (2 );

As mentioned above, all objects inherit empty objects. Therefore, we construct a prototype chain:

We can see that the prototype of the sub-object points to the instance of the parent object, forming the prototype chain of the constructor. The internal proto object of the sub-instance is also the instance pointing to the parent object, which constitutes the internal prototype chain. When we need to find an attribute, the code is similar

The Code is as follows:

Function getAttrFromObj (attr, obj ){

If (typeof (obj) = "object "){

Var proto = obj;

While (proto ){

If (proto. hasOwnProperty (attr )){

Return proto [attr];

}

Proto = proto. _ proto __;

}

}

Return undefined;

}

In this example, if we look for the name attribute in dog, it will look for it in the member list in dog, of course, it will not be found, because currently, only the age option is available in the dog member list. Next, it will follow the prototype chain, that is, the instance to which the. proto points, that is, animal, locate the name attribute and return it. If you are looking for an attribute that does not exist, it will continue when it cannot be found in animal. proto search, find an empty object, and continue following. proto, but empty object. proto points to null and looks for exit.

We raised a question when talking about prototype inheritance during prototype chain maintenance. When method 1 is used to construct inheritance, the constructor of the sub-object instance points to the parent object. The advantage is that we can access the prototype chain through the constructor attribute, and the disadvantage is obvious. An object, the instance it generates should point to itself, that is

The Code is as follows:

(New obj (). prototype. constructor = obj;

Then, after we override the prototype attribute, the constructor of the Instance generated by the sub-object does not point to itself! This is contrary to the original intention of the constructor. We mentioned a solution above:

The Code is as follows:

Dog. prototype = new Animal ("wangwang ");

Dog. prototype. constructor = Dog;

It seems that there is no problem. But in fact, this brings about a new problem, because we will find that we cannot trace back the prototype chain, because we cannot find the parent object, but the internal prototype chain. the proto attribute cannot be accessed. Therefore, SpiderMonkey provides an improved solution: an attribute named _ proto _ is added to any created object, which always points to the prototype used by the constructor. In this way, any changes to the constructor will not affect the value of _ proto _, so it is easy to maintain the constructor.

However, there are two more problems:

_ Proto _ can be rewritten, which means there is still a risk of using it.

_ Proto _ is a special processing of spiderMonkey, which cannot be used in other engines (such as JScript.

Another way is to maintain the properties of the prototype constructor and initialize the constructor attributes of the instance in the subclass constructor function.

The Code is as follows: rewrite the sub-Object

The Code is as follows:

Function Dog (age ){

This. constructor = arguments. callee;

This. age = age;

}

Dog. prototype = new Animal ("wangwang ");

In this way, the constructor of all sub-object instances points to this object correctly, while the prototype constructor points to the parent object. Although the efficiency of this method is relatively low, because the constructor attribute must be rewritten every time an instance is constructed, there is no doubt that this method can effectively solve the previous conflicts. ES5 has taken this situation into consideration and completely solved this problem: the Object can be used at any time. getPrototypeOf () to obtain the real prototype of an object without accessing the constructor or maintaining the external prototype chain. Therefore, we can rewrite the search object attribute as described in the previous section as follows:

The Code is as follows:

Function getAttrFromObj (attr, obj ){

If (typeof (obj) = "object "){

Do {

Var proto = Object. getPrototypeOf (dog );

If (proto [attr]) {

Return proto [attr];

}

}

While (proto );

}

Return undefined;

}

Of course, this method can only be used in browsers that support ES5. For backward compatibility, we still need to consider the previous method. The more appropriate method is to integrate these two methods. I believe that the readers are very good at it and it will not be ugly here.

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.