Javascript prototype chain

Source: Internet
Author: User

The prototype concept in JavaScript exactly reflects the content of the word. We cannot understand it as a pre-declared concept of prototype in C ++.
All function objects in JavaScript have a prototype attribute. This prototype attribute is an object type object. Therefore, we can add any attributes and methods to this prototype object. Since prototype is the object's "prototype", all objects constructed by this function will have this "prototype" feature. In fact, all the attributes and methods defined on the prototype of the constructor can be directly accessed and called through the constructed object. It can also be said that prototype provides a group of similar objects to share attributes and methods.
Let's take a look at the following code:
Function Person (name)
{
This. name = name; // Sets object attributes. Each object has an attribute data.
};
Person. prototype. SayHello = function () // Add the SayHello Method to the prototype of the Person function.
{
Alert ("Hello, I'm" + this. name );
}
Var BillGates = new Person ("Bill Gates"); // create a BillGates object
Var SteveJobs = new Person ("Steve Jobs"); // create a SteveJobs object
BillGates. SayHello (); // call the SayHello method directly through the BillGates object
SteveJobs. SayHello (); // call the SayHello method directly through the SteveJobs object
Alert (BillGates. SayHello = SteveJobs. SayHello); // because the two objects share the prototype of SayHello, true is displayed.
The program running result indicates that the methods defined on the prototype of the constructor can be called directly through the object, and the code is shared. Obviously, setting methods to prototype is much more elegant. Although the call form has not changed, the logic shows the relationship between methods and classes. Compared with the previous method, it is easier to understand and organize code.
So what about the multi-level constructor?
Let's take a look at the following code:
1 function Person (name) // base class Constructor
2 {
3 this. name = name;
4 };
5
6 Person. prototype. SayHello = function () // Add method to prototype of the base class Constructor
7 {
8 alert ("Hello, I'm" + this. name );
9 };
10
11 function Employee (name, salary) // subclass Constructor
12 {
13 Person. call (this, name); // call the base class Constructor
14 this. salary = salary;
15 };
16
17 Employee. prototype = new Person (); // create a base class object as the prototype of the subclass prototype. It is very interesting here
18
19 Employee. prototype. ShowMeTheMoney = function () // Add prototype to constructor to subclass
20 {
21 alert (this. name + "$" + this. salary );
22 };
23
24 var BillGates = new Person ("Bill Gates"); // create a BillGates object for the base class Person
25 var SteveJobs = new Employee ("Steve Jobs", 1234); // create the SteveJobs object of the subclass Employee
26
27 BillGates. SayHello (); // call the prototype method directly through an object
28 SteveJobs. SayHello (); // directly call the prototype method of the base class through the subclass object!
29 SteveJobs. ShowMeTheMoney (); // directly call the prototype method of the subclass through the subclass object
30
31 alert (BillGates. SayHello = SteveJobs. SayHello); // display: true, indicating that the prototype method is shared.
Line 2 of this Code constructs a base class object and sets it as the prototype of the subclass constructor. This is very interesting. The purpose of this operation is to set the number of rows. You can directly call the prototype method of the base class through the subclass object. Why?
Originally, in JavaScript, prototype not only allows objects to share their wealth, but also allows prototype to search for the ancestor's nature, so that the inheritance of the elders can be passed on from generation to generation. When reading a property from an object or calling a method, if the object itself does not have such a property or method, it will search for its associated prototype object; If prototype does not exist, it will also search for prototype associated with its predecessor prototype until the process of finding or tracing is completed.
In JavaScript, object attributes and Method Tracing mechanisms are implemented through the so-called prototype chain. When an object is constructed using the new operator, the prototype object of the constructor is also assigned to the newly created object to become the built-in prototype object of the object. The built-in prototype object of the object should be invisible to the outside world. Although some browsers (such as Firefox) allow us to access this built-in prototype object, it is not recommended to do so. The built-in prototype object itself is also an object and also has its own associated prototype object, thus forming a so-called prototype chain.
At the very end of the prototype chain, it is the prototype Object pointed to by the prototype property of the Object constructor. This prototype object is the oldest ancestor of all objects. This ancestor implements methods that are inherent to all objects such as toString. Prototypes of other built-in constructors, such as Function, Boolean, String, Date, and RegExp, are inherited from this ancestor, But they define their own attributes and methods, as a result, their children show the characteristics of their respective clan.
Isn't that "inheritance? Yes, this is "inheritance", which is unique to JavaScript "Prototype inheritance ".
Prototype inheritance is kind and strict. The prototype objects selflessly contribute their attributes and methods to the children, and do not force the children to follow them, allowing some naughty children to act independently based on their interests and hobbies. From this point of view, the prototype object is a kind mother. However, although a child can do anything, it cannot manipulate the property of the prototype object, because it may affect the interests of other children. From this point of view, the prototype object is like a strict father. Let's take a look at the following code to understand this meaning:
Function Person (name)
{
This. name = name;
};
Person. prototype. company = "Microsoft"; // prototype attributes
Person. prototype. SayHello = function () // prototype Method
{
Alert ("Hello, I'm" + this. name + "of" + this. company );
};
Var BillGates = new Person ("Bill Gates ");
BillGates. SayHello (); // according to the prototype, the output is Hello, I'm Bill Gates.
Var SteveJobs = new Person ("Steve Jobs ");
SteveJobs. company = "Apple"; // you can set your own company attributes to conceal the original company attributes.
SteveJobs. SayHello = function () // implements its own SayHello method, which masks the prototype SayHello method.
{
Alert ("Hi," + this. name + "like" + this. company + ", ha ha ");
};
SteveJobs. SayHello (); // all attributes and methods covered by you. Output: Hi, Steve Jobs like Apple, ha ha ha
BillGates. SayHello (); // The overwrite of SteveJobs does not affect the prototype object. The BillGates output is as follows:
Objects can mask the attributes and methods of the prototype object. A constructor prototype object can also mask the existing attributes and methods of the upper-level constructor prototype object. In fact, this masking only creates new attributes and methods on the object itself, but these attributes and methods have the same names as those of the prototype object. JavaScript uses this simple masking mechanism to implement object "polymorphism", which is consistent with the concept of static object language virtual functions and override.
However, what's more amazing than static object language is that we can dynamically add new attributes and methods to the prototype at any time to dynamically expand the functional features of the base class. This is hard to imagine in the static object language. Let's look at the following code:
Function Person (name)
{
This. name = name;
};
Person. prototype. SayHello = function () // method defined before object Creation
{
Alert ("Hello, I'm" + this. name );
};
Var BillGates = new Person ("Bill Gates"); // create an object
BillGates. SayHello ();
Person. prototype. Retire = function () // Method for dynamically expanding the prototype after an object is created
{
Alert ("Poor" + this. name + ", bye! ");
};
BillGates. Retire (); // the method of dynamic expansion can be immediately called by the previously created object.
Amimayfo, prototype inheritance can actually play with such a spell!
Prototype Extension
Presumably, you may think like this: if you add new methods and attributes to the prototype of functions built in JavaScript, such as objects and functions, is it possible to expand the JavaScript function?
Congratulations! You got it!
With the rapid development of AJAX technology, many successful JavaScript runtime libraries of AJAX projects have greatly extended the prototype function of built-in functions. For example, Microsoft's ASP. net ajax adds a large number of new features to these built-in functions and their prototypes, thus enhancing JavaScript Functions.
Let's take a look at the code from MicrosoftAjax. debug. js:
String. prototype. trim = function String $ trim (){
If (arguments. length! = 0) throw Error. parameterCount ();
Return this. replace (/^ \ s + | \ s + $/g ,'');
}
This Code extends the trim method to prototype of the built-in String function, so all the String class objects have the trim method. With this extension, we need to remove the white space of the string segments in the future, so we don't need to process them separately, because any string has this extension function, which is really convenient to call.
Of course, few people add methods to the prototype of the Object, because it will affect all objects, unless in your architecture, this method is indeed required by all objects.
In the past two years, at the beginning of designing the AJAX class library, Microsoft used a technology called "closure" to simulate "classes ". The general model is as follows:
Function Person (firstName, lastName, age)
{
// Private variable:
Var _ firstName = firstName;
Var _ lastName = lastName;
// Public variables:
This. age = age;
// Method:
This. getName = function ()
{
Return (firstName + "" + lastName );
};
This. SayHello = function ()
{
Alert ("Hello, I'm" + firstName + "" + lastName );
};
};
Var BillGates = new Person ("Bill", "Gates", 53 );
Var SteveJobs = new Person ("Steve", "Jobs", 53 );
BillGates. SayHello ();
SteveJobs. SayHello ();
Alert (BillGates. getName () + "" + BillGates. age );
Alert (BillGates. firstName); // Private variables cannot be accessed here
Obviously, the class description of this model is very similar to the description form of C # language. It is very elegant to define private members, public attributes, and available methods in one constructor. In particular, the "closure" mechanism can simulate the protection mechanism for private members, which is very beautiful.
The so-called "closure" refers to defining another function in the constructor as the method function of the target object, and the method function of this object references temporary variables in the outer function body in turn. This allows the target object to indirectly retain the temporary variable value used by the original constructor as long as it can maintain its method throughout the lifetime. Although the call to the initial constructor has ended, the name of the Temporary Variable disappears, but the value of the variable can always be referenced in the method of the target object, this value can only be accessed in this way. Even if the same constructor is called again, only new objects and methods are generated. The new temporary variables only correspond to new values, which are independent of those called last time. Really clever!
However, as we mentioned earlier, setting a method for each object is a great waste. In addition, the "closure" mechanism that indirectly maintains the variable value often creates difficulties for the garbage collector of JavaSript. Especially when complex cyclic references between objects are encountered, the judgment logic of garbage collection is very complicated. Coincidentally, earlier versions of Internet Explorer did have memory leakage problems in the garbage collection process of JavaSript. Coupled with the poor performance of the "closure" Model in performance testing, Microsoft finally gave up the "closure" model and switched to the "prototype" model. The so-called "there will be loss.
The prototype model requires a constructor to define the object members, but the methods are attached to the constructor's prototype. The statement is as follows:
// Define the constructor
Function Person (name)
{
This. name = name; // define a member in the constructor
};
// Define the method to the prototype of the constructor.
Person. prototype. SayHello = function ()
{
Alert ("Hello, I'm" + this. name );
};
// Subclass Constructor
Function Employee (name, salary)
{
Person. call (this, name); // call the upper-layer constructor.
This. salary = salary; // extended member
};
// The subclass constructor first needs to use the upper-layer constructor to establish the prototype object and implement the concept of inheritance.
Employee. prototype = new Person () // only the prototype method is required. The members of this object have no meaning!
// The subclass method is also defined on the constructor.
Employee. prototype. ShowMeTheMoney = function ()
{
Alert (this. name + "$" + this. salary );
};
Var BillGates = new Person ("Bill Gates ");
BillGates. SayHello ();
Var SteveJobs = new Employee ("Steve Jobs", 1234 );
SteveJobs. SayHello ();
SteveJobs. ShowMeTheMoney ();
Although the prototype class model cannot simulate real private variables, it also has to define classes in two parts, which is not very "elegant ". However, the methods between objects are shared, there is no garbage collection problem, and the performance is superior to the closure model. The so-called "Gains and Losses.
In the prototype model, prototype of the subclass constructor must be set as an object instance of the parent class to implement class inheritance. The purpose of creating this parent class object instance is to form a prototype chain and share the upper-layer prototype method. However, when this instance object is created, the upper-layer constructor will also set object members for it. These object members are meaningless for inheritance. Although we didn't pass parameters to the constructor, we did create some useless members, even though the value is undefined, which is also a waste.
Alas! There is no perfect thing in the world!
Prototype
Just as we were excited, a red light flashed in the sky, and the Goddess of Mercy appeared in Xiangyun. I saw her holding the jade bottle, gently flat the willow branches, sprinkling a few drops of nectar, and suddenly let JavaScript new aura.
The canonicalized dew in the JavaScript world has become a kind of thing called "syntax Dew. This syntax makes the code we write look more like an object language.
If you want to know what the "Syntax" is, please listen carefully.
Before understanding these syntaxes, we need to review the process of constructing objects in JavaScript.
We already know that the process of creating an object in the form of var anObject = new aFunction () can be divided into three steps: the first step is to create a new object; step 2: Set the built-in prototype object of the object to the prototype object referenced by the constructor prototype. Step 3: Call the constructor as the this parameter to complete initialization such as member settings. After the object is created, any access and operations on the object are only related to the object itself and the string of objects on its prototype chain, and cannot be connected to the constructor. In other words, constructor only introduces the prototype object and initialization object when creating an object.
So can we define an object as a prototype, describe the class on this prototype, and then set this prototype to the newly created object and treat it as the class of the object? Can we use a method in this prototype as a constructor to initialize the newly created object? For example, we define a prototype object:
Var Person = // define an object as a prototype
{
Create: function (name, age) // This is the constructor.
{
This. name = name;
This. age = age;
},
SayHello: function () // definition method
{
Alert ("Hello, I'm" + this. name );
},
HowOld: function () // definition method
{
Alert (this. name + "is" + this. age + "years old .");
}
};
What a C # class is written in JSON format! There are both constructors and various methods. If you can create an object in some form and set the built-in prototype of the object to the above "class" object, isn't it equivalent to creating an object of this class?
Unfortunately, we almost cannot access the built-in prototype attributes of the object! Although some browsers can access the built-in prototype of objects, this only limits the browser that users must use. This is almost impossible.
So, can we use a function object as the media, use the prototype attribute of the function object to transit this prototype, and use the new operator to pass it to the new object?
In fact, code like this can achieve this goal:
Function anyfunc () {}; // defines a function shell.
Anyfunc. prototype = Person; // place the prototype object in the transfer station prototype
Var BillGates = new anyfunc (); // the built-in prototype of the newly created object will be the expected prototype object.
However, this anyfunc function is just a shell. After using this shell, it becomes a superfluous thing, and it is not different from directly using constructors to create objects.
However, if we write the code as a general function, and the function shell becomes a function in the function, can this internal function automatically disappear after the outer function exits the scope? In addition, we can take the prototype object as the parameter of the common function, so that the common function can return the created object. What we need is the following form:
Function New (aClass, aParams) // create a function
{
Function new _ () // defines a temporary transit function Shell
{
AClass. Create. apply (this, aParams); // call the constructor defined in the prototype, and use the intermediate constructor logic and constructor parameters.
};
New _. prototype = aClass; // prepare the intermediate prototype object
Return new _ (); // return the object created
};
Var Person = // defined class
{
Create: function (name, age)
{
This. name = name;
This. age = age;
},
SayHello: function ()
{
Alert ("Hello, I'm" + this. name );
},
HowOld: function ()
{
Alert (this. name + "is" + this. age + "years old .");
}
};
Var BillGates = New (Person, ["Bill Gates", 53]); // call a common function to create an object and pass the constructor parameters in an array.
BillGates. SayHello ();
BillGates. HowOld ();
Alert (BillGates. constructor = Object); // output: true
The general function New () Here is a "Syntax "! The syntax ganlu not only transits the prototype object, but also the constructor logic and constructor parameters.
Interestingly, the temporary New _ function object is automatically released every time the created Object exits the new function scope. Because the prototype attribute of new _ is set as a new prototype object, the reference chain has been removed between the original prototype object and new, temporary functions and their original prototype objects will be correctly recycled. The last sentence of the code above proves that the constructor attribute of the newly created Object returns the Object function. In fact, the newly created Object itself and its prototype do not have the constructor attribute. The returned result is only the constructor of the top-level prototype Object, that is, the Object.
With the New syntax, the class definition is like the form of C # Those static object languages. How quiet and elegant the code looks!
Of course, this code only shows the concept of "Syntax. We need more syntax to write the class hierarchy and Its Inheritance relationships with concise and elegant code. Now let's look at a richer example:
// Syntax:
Var object = // defines basic classes of lower-case objects for the most basic methods.
{
IsA: function (aType) // a basic method for judging the relationship between classes and between objects and Classes
{
Var self = this;
While (self)
{
If (self = aType)
Return true;
Self = self. Type;
};
Return false;
}
};
Function Class (aBaseClass, aClassDefine) // creates a Class function to declare the Class and its inheritance relationship.
{
Function class _ () // create a temporary function shell for the class
{
This. Type = aBaseClass; // we specify a Type attribute for each class and reference its inherited class.
For (var member in aClassDefine)
This [member] = aClassDefine [member]; // all the replication classes are defined as the currently created classes.
};
Class _. prototype = aBaseClass;
Return new class _();
};
Function New (aClass, aParams) // creates an object function, used to create any Class Object
{
Function new _ () // create a temporary function shell for the object
{
This. Type = aClass; // We also specify a Type attribute for each object to access the class to which the object belongs.
If (aClass. Create)
AClass. Create. apply (this, aParams); // we agree that the constructors of all classes are called Create, which is similar to DELPHI.
};
New _. prototype = aClass;
Return new _();
};
// Apply the following syntax:
Var Person = Class (object, // derived from the basic object Class
{
Create: function (name, age)
{
This. name = name;
This. age = age;
},
SayHello: function ()
{
Alert ("Hello, I'm" + this. name + "," + this. age + "years old .");
}
});
Var Employee = Class (Person, // derived to the Person Class, is it very similar to the general object language?
{
Create: function (name, age, salary)
{
Person. Create. call (this, name, age); // call the constructors of the base class
This. salary = salary;
},
ShowMeTheMoney: function ()
{
Alert (this. name + "$" + this. salary );
}
});
Var BillGates = New (Person, ["Bill Gates", 53]);
Var SteveJobs = New (Employee, ["Steve Jobs", 53,123 4]);
BillGates. SayHello ();
SteveJobs. SayHello ();
SteveJobs. ShowMeTheMoney ();
Var LittleBill = New (BillGates. Type, ["Little Bill", 6]); // create LittleBill Based on the BillGate Type
LittleBill. SayHello ();
Alert (BillGates. isA (Person); // true
Alert (BillGates. isA (Employee); // false
Alert (SteveJobs. isA (Person); // true
Alert (Person. isA (Employee); // false
Alert (Employee. isA (Person); // true
There is no need for "Syntax". Just a little bit, you can change the ease of coding and smoothness of the entire code, so that the code looks more elegant. With these syntaxes, JavaScript is much like a common object language, and it feels much better to write code!
Fortunately, JavaScript programs that are nourished by the nectar are more efficient. Because the prototype object does not have any useful object-level members and does not have the constructor attribute body, it is less involved with the constructor, however, the sharing of methods is still maintained. This makes it much less time for JavaScript to trace prototype links and search attributes and methods.
Let's call this form the "ganlu model! In fact, the prototype usage of this "ganlu model" is in line with the prototype concept, and is the true meaning of the JavaScript prototype!
Presumably, when Microsoft's engineers designed the AJAX architecture saw this nectar model, they certainly regretted not moving the AJAX department from the United States to the Guanyin Temple in China, and missed the point-of-the-Goddess of Mercy. Of course, we can only play with Bill Gates as an object in the code example. It is not easy to let him give up God and switch to my Buddha! If you see this nectar model in the new AJAX Class Library released by Microsoft one day, that is the true fate!
Happy Programming
Today, with the rapid development of the software industry, various programming languages are emerging one after another. The birth of new languages and the evolution of old languages seem to have dazzled us. In order to adapt to the trend of object-oriented programming, the JavaScript language is also moving towards full object-oriented development. The new JavaScript standard has extended many new object-oriented elements in terms of semantics. On the contrary, many static object languages are also moving towards the concise and elegant direction of JavaScript. For example, the new C # language absorbs the concise notation like JSON and some other JavaScript features.
As we can see, with the development and popularization of RIA, AJAX technology will gradually fade out, and JavaScript will eventually disappear or evolve into other forms of language. However, no matter how the programming language develops and evolves, the programming world will always maintain infinite vitality in the tangle of "data" and "code. As long as we can see through this, we can easily learn and understand the various new things in the software world. Whether we are familiar with procedural programming, developing functional programming, and large-scale parallel programming of future quantum entangled states, we have enough mana to solve all the complicated problems.

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.