Prototype is a magical feature for beginners of JavaScript. In fact, prototype is of great significance to JavaScript. prototype is not only a mechanism for managing Object Inheritance, it is also an outstanding design idea. The prototype attribute of the object in JavaScript, which can return the reference of the Object Type prototype. This is a pretty straightforward explanation. To understand it, you must first correctly understand the concepts of object types and prototypes.
1. What is prototype?
The prototype attribute of the object in JavaScript, which can return the reference of the Object Type prototype. This is a pretty straightforward explanation. To understand it, you must first correctly understand the concepts of object types and prototypes.
As we mentioned above, the Class of an object and the Instance of an object are a "creation" relationship. Therefore, we regard the Class as a modeling of object features, objects are treated as concrete features of classes, or classes are a Type of objects ). For example, in the previous example, the p1 and p2 types are both Point. In JavaScript, The instanceof operator can verify this Point:
P1 instanceof Point
P2 instanceof Point
However, Point is not the only type of p1 and p2, because p1 and p2 are both objects, so Obejct is also their type, because Object is a more generalized class than Point, so we say, there is a derivative relationship between Obejct and Point. We will know later that this relationship is called "inheritance" and it is also a special case of the generalized relationship between objects, it is an indispensable basic relationship in object-oriented systems.
In the Object-Oriented field, instances and types are not the only one pair of descriptive abstract relationships. In JavaScript, another important abstract relationship is Type) and prototype ). This relationship is a high-level abstract relationship. It Exactly forms a three-tier chain with the abstract relationship between types and instances.
In real life, we often say that something is created based on another thing. These two items can be of the same type or different types. The idiom "depends on the gourd painting". Here the gourd is the prototype, while the gourd is the type. It is represented by the prototype in JavaScript. prototype = a gourd "or" gourd. prototype = new gourd ()".
To thoroughly understand the prototype, you can study a design pattern of prototype pattern. The core of this pattern is to use the prototype instance to specify the type of the object to be created, and create new objects by copying these prototypes. The prototype of JavaScript is similar to this method.
For more information about prototype pattern, see Design Patterns.
Note that the relationship between the same type and the instance is different, the relationship between a prototype and a type requires that one type can only have one prototype at a time point (while an instance can obviously have multiple types at a time point ). For JavaScript, this restriction has two meanings. The first is that each specific JavaScript type has only one prototype. By default, this prototype is an Object (note that it is not of the Object Type !). Second, the object type must be a type chain that satisfies the prototype relationship. For example, p1 belongs to the type of Point and Object, while an Object is a prototype of Point. If an Object belongs to the following types: ClassA, ClassB, ClassC, and Object, the four classes must constitute a complete prototype chain.
Interestingly, JavaScript does not specify the type of a prototype (which is a very colloquial term), so it can be any type, usually an object. In this way, object-type-prototype (object) may constitute a ring structure or other interesting topology structures. These structures provide a wide variety of usage for JavaScript, some of these uses are both clever and aesthetic. The following section describes how to use prototype.
2 prototype usage tips
Before learning how to use prototype, first understand the features of prototype. First, JavaScript provides a prototype attribute for each Type, pointing this attribute to an object, and this object becomes a prototype of this Type ", this means that all objects created by this type have the characteristics of this prototype. In addition, JavaScript objects are dynamic, and prototype is no exception. Adding or decreasing properties to prototype will change the prototype, this change will directly apply to all objects created by this prototype, for example:
The Code is as follows:
Script
Function Point (x, y)
{
This. x = x;
This. y = y;
}
Var p1 = new Point (1, 2 );
Var p2 = new Point (3, 4 );
Point. prototype. z = 0; // attributes are dynamically added to the prototype of the Point.
Alert (p1.z );
Alert (p2.z); // all objects created for the Point type at the same time
Script
If a property named a is added to the prototype of an object and the object itself has an attribute of the same name as a, When you access attribute a of this object, the property of the object overwrites the prototype property, but the prototype property does not disappear. When you delete attribute a of the object using the delete operator, the object's prototype property restores visibility. With this feature, you can set the default value for the object property, for example:
The Code is as follows:
Script
Function Point (x, y)
{
If (x) this. x = x;
If (y) this. y = y;
}
Point. prototype. x = 0;
Point. prototype. y = 0;
Var p1 = new Point;
Var p2 = new Point (1, 2 );
Script
In the above example, the default value (0, 0) is set for the Point object through prototype. Therefore, the p1 value is (0, 0), the p2 value is (1, 2), and the delete p2.x, delete p2.y; the p2 value can be restored to (0, 0 ). The following is a more interesting example:
The Code is as follows:
Script
Function classA ()
{
This. a = 100;
This. B = 200;
This. c = 300;
This. reset = function ()
{
For (var each in this)
{
Delete this [each];
}
}
}
ClassA. prototype = new classA ();
Var a = new classA ();
Alert (a. );
A. a * = 2;
A. B * = 2;
A. c * = 2;
Alert (a. );
Alert (a. B );
Alert (a. c );
A. reset (); // call the reset method to restore the value of a to the default value.
Alert (a. );
Alert (a. B );
Alert (a. c );
Script
Prototype can also be used to set a read-only getter for the object attribute to avoid rewriting. The following is an example:
The Code is as follows:
Script
Function Point (x, y)
{
If (x) this. x = x;
If (y) this. y = y;
}
Point. prototype. x = 0;
Point. prototype. y = 0;
Function LineSegment (p1, p2)
{
// Private member
Var m_firstPoint = p1;
Var m_lastPoint = p2;
Var m_width = {
ValueOf: function () {return Math. abs (p1.x-p2.x )},
ToString: function () {return Math. abs (p1.x-p2.x )}
}
Var m_height = {
ValueOf: function () {return Math. abs (p1.y-p2.y )},
ToString: function () {return Math. abs (p1.y-p2.y )}
}
// Getter
This. getFirstPoint = function ()
{
Return m_firstPoint;
}
This. getLastPoint = function ()
{
Return m_lastPoint;
}
This. length = {
ValueOf: function () {return Math. sqrt (m_width * m_width + m_height * m_height )},
ToString: function () {return Math. sqrt (m_width * m_width + m_height * m_height )}
}
}
Var p1 = new Point;
Var p2 = new Point (2, 3 );
Var line1 = new LineSegment (p1, p2 );
Var lp = line1.getFirstPoint ();
Lp. x = 100; // accidentally rewrite the lp value, destroys the original lp value and cannot be recovered.
Alert (line1.getFirstPoint (). x );
Alert (line1.length); // even line1.lenght has changed
Script
Rewrite this. getFirstPoint () as follows:
The Code is as follows:
This. getFirstPoint = function ()
{
Function GETTER (){};
GETTER. prototype = m_firstPoint;
Return new GETTER ();
}
This problem can be avoided, ensuring the read-only attribute of m_firstPoint.
The Code is as follows:
Script
Function Point (x, y)
{
If (x) this. x = x;
If (y) this. y = y;
}
Point. prototype. x = 0;
Point. prototype. y = 0;
Function LineSegment (p1, p2)
{
// Private member
Var m_firstPoint = p1;
Var m_lastPoint = p2;
Var m_width = {
ValueOf: function () {return Math. abs (p1.x-p2.x )},
ToString: function () {return Math. abs (p1.x-p2.x )}
}
Var m_height = {
ValueOf: function () {return Math. abs (p1.y-p2.y )},
ToString: function () {return Math. abs (p1.y-p2.y )}
}
// Getter
This. getFirstPoint = function ()
{
Function GETTER (){};
GETTER. prototype = m_firstPoint;
Return new GETTER ();
}
This. getLastPoint = function ()
{
Function GETTER (){};
GETTER. prototype = m_lastPoint;
Return new GETTER ();
}
This. length = {
ValueOf: function () {return Math. sqrt (m_width * m_width + m_height * m_height )},
ToString: function () {return Math. sqrt (m_width * m_width + m_height * m_height )}
}
}
Var p1 = new Point;
Var p2 = new Point (2, 3 );
Var line1 = new LineSegment (p1, p2 );
Var lp = line1.getFirstPoint ();
Lp. x = 100; // The lp value is accidentally rewritten, but the original value is not damaged.
Alert (line1.getFirstPoint (). x );
Alert (line1.length); // line1.lenght does not change
Script
In fact, setting an object as a prototype is equivalent to creating a read-only copy of the object by instantiating this type. Changing the copy at any time will not affect the original object, changing the original object will affect the copy, unless the changed property has been overwritten by the copy's own attributes with the same name. If you delete an object with the same name, the visibility of the prototype property can be restored. Here is another example:
The Code is as follows:
Script
Function Polygon ()
{
Var m_points = [];
M_points = Array. apply (m_points, arguments );
Function GETTER (){};
GETTER. prototype = m_points [0];
This. firstPoint = new GETTER ();
This. length = {
ValueOf: function () {return m_points.length },
ToString: function () {return m_points.length}
}
This. add = function (){
M_points.push.apply (m_points, arguments );
}
This. getPoint = function (idx)
{
Return m_points [idx];
}
This. setPoint = function (idx, point)
{
If (m_points [idx] = null)
{
M_points [idx] = point;
}
Else
{
M_points [idx]. x = point. x;
M_points [idx]. y = point. y;
}
}
}
Var p = new Polygon ({x: 1, y: 2}, {x: 2, y: 4}, {x: 2, y: 6 });
Alert (p. length );
Alert (p. firstPoint. x );
Alert (p. firstPoint. y );
P. firstPoint. x = 100; // The value is accidentally written.
Alert (p. getPoint (0). x); // The actual private member is not affected.
Delete p. firstPoint. x; // restore
Alert (p. firstPoint. x );
P. setPoint (0, {x: 3, y: 4}); // rewrite the actual private member through setter
Alert (p. firstPoint. x); // The value of getter is changed.
Alert (p. getPoint (0). x );
Script
Note: The preceding example shows that prototype can be used to quickly create multiple copies of an object. Generally, prototype is used to create a large number of complex objects, it is much faster than copying objects using any other method. Note that using an object as the prototype to create a large number of new objects is exactly the essence of prototype pattern.
The following is an example:
The Code is as follows:
Script
Var p1 = new Point (1, 2 );
Var points = [];
Var PointPrototype = function (){};
PointPrototype. prototype = p1;
For (var I = 0; I <10000; I ++)
{
Points [I] = new PointPrototype ();
// Because the PointPrototype constructor is an empty function, its construction is much faster than directly constructing a // p1 copy.
}
Script
In addition to the usage techniques mentioned above, prototype is used for its unique features and other purposes. It may be used to simulate inheritance, we will discuss this in the next section.
3 prototype
The role of prototype has been discussed above. Now we can reveal the essence of prototype through laws.
We say that prototype acts like a static field in C ++ and adds an attribute as a prototype attribute, which will be shared by all instances created for this type, however, such sharing is read-only. In any instance, you can only overwrite this attribute with the same name, but cannot change it. In other words, when an object reads an attribute, it always checks the Attribute Table of its own domain. If this attribute exists, it will return this attribute; otherwise, it will read the prototype field, returns the properties of the protoype field. In addition, JavaScript allows the protoype field to reference any type of objects. Therefore, if this attribute is still not found for reading the protoype field, javaScript recursively searches for the prototype field of the object to which the prototype field points until the prototype field of the object is itself or appears in a loop, we can use the following figure to describe the relationship between prototype and the object instance:
// TODO:
4 value and limitations of prototype
From the above analysis, we understand prototype. It can be used as a prototype to securely create a large number of instances. This is the true meaning of prototype and its value. We will see later that prototype can be used to simulate Object Inheritance. However, prototype is used to simulate inheritance, although it is also an important value, but it is definitely not its core. In other words, JavaScript supports prototype instead of just implementing its object inheritance. Even without prototype inheritance, the prototype mechanism of JavaScript is still very useful.
Prototype only uses objects as the prototype to build copies for the type, so it also has great limitations. First, it does not represent a value copy in the prototype field, but a reference copy, which brings "Side effects ". Changing the attribute value of the referenced type attribute on a prototype (which is equivalent to the parameter value P) will completely affect every instance created for this type. Sometimes this is exactly what we need (for example, changing the default value of all objects in a class), but sometimes this is what we don't want (for example, when class inheritance is used ), the following is an example:
The Code is as follows:
Script
Function ClassA ()
{
This. a = [];
}
Function ClassB ()
{
This. B = function (){};
}
ClassB. prototype = new ClassA ();
Var objB1 = new ClassB ();
Var objB2 = new ClassB ();
ObjB1.a. push (1, 2, 3 );
Alert (objB2.a );
// All a members in all B instances have changed !! This is not what we want to see in this example.
Script
JavaScript implementation:
In Java, objects are inherited from java. lang. object, while java. lang. the Object provides the Clone method. If the interface Cloneable is implemented, Clone is supported. Otherwise, an exception is thrown. At this point, JavaScript is very similar. All objects are inherited from objects. However, objects do not support the Clone method, but we can implement the Clone method through expanddo for JavaScript, in this way, the Clone method is implemented for all subsequent object creation.
Because JavaScript itself does not provide the Clone method, and the Object is assigned a value such as var a = new Object (); var B = a, such code a and B point to the same Object, to create an object, you must use the new keyword. Therefore, in the Clone implementation process, I internally defined a constructor CloneModel, at the same time, specify the parent object as the object for the Clone activity. Therefore, we use the this keyword to create an object based on the defined structure of the sub-CloneModel, because there is no code inside the constructor, the newly created object actually says that all implementations are in the parent object, that is, the object we need to Clone. So far, we have created an object to be copied, but all the values point to the parent object.
In the object-oriented method of JavaScript, we have discussed that if the value of the parent object is not overwritten, it directly points to the parent object, prototype Pattern requires that the internal value of the cloned object should not be relevant. As long as the value is assigned once, the value of objClone will be in its own memory space, rather than pointing to the parent object. Based on this consideration, the objClone [v] = objClone [v]; statement is to copy the value of the parent object to its memory by overwriting.
21.2.1 what is prototype
The prototype attribute of the object in JavaScript, which can return the reference of the Object Type prototype. This is a pretty straightforward explanation. To understand it, you must first correctly understand the concepts of object types and prototypes. As we mentioned above, the Class of an object and the Instance of an object are a "creation" relationship. Therefore, we regard the Class as a modeling of object features, objects are treated as concrete features of classes, or classes are a Type of objects ). For example, in the previous example, the p1 and p2 types are both Point. In JavaScript, you can verify this by using the instanceof OPERATOR: p1 instanceof Point p2 instanceof Point. However, point is not the only type of p1 and p2, because p1 and p2 are both objects, so Obejct is also their type, because objects are more generalized classes than Point, so we say, there is a derivative relationship between Obejct and Point. We will know later that this relationship is called "inheritance" and it is also a special case of the generalized relationship between objects, it is an indispensable basic relationship in object-oriented systems. In the Object-Oriented field, instances and types are not the only one pair of descriptive abstract relationships. In JavaScript, another important abstract relationship is Type) and prototype ). This relationship is a high-level abstract relationship. It Exactly forms a three-tier chain with the abstract relationship between types and instances,
Figure 21.2 describes the relationship:
Figure 21.2 relationship between objects, types, and prototypes
In real life, we often say that something is created based on another thing. These two items can be of the same type or different types. The idiom "show pictures and tigers". Here the cat is the prototype, and the tiger is the type, which is represented by the prototype in JavaScript. prototype = a cat or tiger. prototype = new CAT ()". Prototype describes the "classification" relationship between natural things. Other relations include "inheritance" and "interface ". In general, "inheritance" describes the inherent derivative relationships between things and has a strong correlation (kinship) between things that can be described by "inheritance ). Interfaces describe the common features of the functions of things. Prototype tends to describe the "similarity" between things ". From this point of view, prototype describes the relevance of things in a broader sense than inheritance and interfaces. If you are a Java programmer, from the inheritance perspective, of course, you cannot use "cat" to inherit "tiger" or "tiger" to inherit "cat ", to describe their relationships, you need to create an "abstract class" that covers their commonalities, or you will call it "cat ". However, if my system only needs to use "cat" and "tiger", this redundant "cat" does not make any sense to me. What I just need to express is, "Tiger" is a bit like "cat", that's all. Here, prototype helps us successfully save a type of "CAT" that is not necessary to be established ". To thoroughly understand the prototype, you can study a design pattern of prototype pattern. The core of this pattern is to use the prototype instance to specify the type of the object to be created, and create new objects by copying these prototypes. The prototype of JavaScript is similar to this method. For details about prototype pattern, refer to Design Patterns, which is not the scope of this book. Note that the prototype requires that one type can have only one prototype at a time point (while one instance can obviously have multiple types at a time point ). For JavaScript, this restriction has two meanings. The first is that each specific JavaScript type has only one prototype. By default, this prototype is an Object (note that it is not of the Object Type !). Second, all types of instances of this type must be type chains that meet the prototype relationships. For example, p1 belongs to the type of Point and Object, while an Object is a prototype of Point. Assume that an Object belongs to the following types: ClassA, ClassB, ClassC, and Object:
Example 21.4 type chain of the prototype relationship
Function ClassA ()
{
......
}
ClassA. prototype = new Object (); // This can be omitted
Function ClassB ()
{
......
}
ClassB. prototype = new ClassA (); // The ClassB is prototype based on the ClassA object.
Function ClassC ()
{
......
}
ClassC. prototype = new ClassB (); // ClassC is based on the ClassB object.
Var obj = new ClassC ();
Alert (obj instanceof ClassC); // true
Alert (obj instanceof ClassB); // true
Alert (obj instanceof ClassA); // true
Alert (obj instanceof Object); // true
Figure 21.3 briefly describes the relationships between them:
Figure 21.3 type chain of prototype relationships
Interestingly, JavaScript does not specify the type of a prototype (which is a very colloquial term), so it can be any type, usually an object. In this way, object-type-prototype (object) may constitute a ring structure or other interesting topology structures. These structures provide a wide variety of usage for JavaScript, some of these uses are both clever and aesthetic. The following section describes how to use prototype. <