It's not as simple as prototype inheritance !! Deep Exploration of prototype 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, describing this relationship:
// TODO:
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. Assume that an Object belongs to the following types: ClassA, ClassB, ClassC, and Object:
// TODO:
The following figure describes the relationship between objects, types, and prototypes in JavaScript:
// TODO:
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:
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; // The attribute alert (p1.z); alert (p2.z) is dynamically added to the prototype of the Point; // all objects created for the Point type are also applied to script
[Ctrl + A select all Note: If you need to introduce external Js, You need to refresh it to execute]
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:
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
[Ctrl + A select all Note: If you need to introduce external Js, You need to refresh it to execute]
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:
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 * = 2;. B * = 2;. c * = 2; alert (. a); alert (. b); alert (. c);. reset (); // call the reset method to restore the value of a to the default alert (. a); alert (. b); alert (. c); script
[Ctrl + A select all Note: If you need to introduce external Js, You need to refresh it to execute]
Prototype can also be used to set a read-only getter for the object attribute to avoid rewriting. The following is an example:
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 restore alert (line1.getFirstPoint (). x); alert (line1.length); // even line1.lenght changes script.
[Ctrl + A select all Note: If you need to introduce external Js, You need to refresh it to execute]
Rewrite this. getFirstPoint () 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.
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 alert (line1.getFirstPoint () is not damaged (). x); alert (line1.length); // line1.lenght does not change script
[Ctrl + A select all Note: If you need to introduce external Js, You need to refresh it to execute]
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:
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; // accidentally write its value alert (p. getPoint (0 ). x); // does not affect the actual private member delete p. firstPoint. x; // restore alert (p. firstPoint. x); p. setPoint (0, {x: 3, y: 4}); // rewrite the actual private member alert (p. firstPoint. x); // The value of getter has changed alert (p. getPoint (0 ). x); script
[Ctrl + A select all Note: If you need to introduce external Js, You need to refresh it to execute]
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:
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
[Ctrl + A select all Note: If you need to introduce external Js, You need to refresh it to execute]
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:
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 (, 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
[Ctrl + A select all Note: If you need to introduce external Js, You need to refresh it to execute]