Home >Web Front-end >JS Tutorial >JS object-oriented magic prototype_javascript technique

JS object-oriented magic prototype_javascript technique

WBOY
WBOYOriginal
2016-05-16 18:10:17800browse

The prototype attribute of an object in JavaScript can return a reference to the prototype of the object type. This is a rather confusing explanation. To understand it, you must first correctly understand the concepts of object type (Type) and prototype (prototype).
1 What is prototype
The prototype attribute of an object in JavaScript can return a reference to the prototype of the object type. This is a rather confusing explanation. To understand it, you must first correctly understand the concepts of object type (Type) and prototype (prototype).
We said earlier that there is a "creation" relationship between the object's class (Class) and the object instance (Instance), so we regard "class" as the modeling of object characteristics, and the object as a class The embodiment of characteristics, or class (Class) is a type (Type) of an object. For example, in the previous example, the types of p1 and p2 are both Point. In JavaScript, this can be verified through the instanceof operator:
p1 instanceof Point
p2 instanceof Point
However, Point is not p1 and p2 are the only types. Because p1 and p2 are both objects, Object is also their type. Because Object is a more general class than Point, we say that there is a derived relationship between Object and Point. We will discuss it later. You know, this relationship is called "inheritance". It is also a special case of the generalized relationship between objects and is an indispensable basic relationship in object-oriented.
In the object-oriented field, instance and type are not the only describable abstract relationship. In JavaScript, another important abstract relationship is type (Type) and prototype (prototype). This relationship is a higher-level abstract relationship, which happens to form a three-layer chain with the abstract relationship between types and instances.
In real life, we often say that something is based on another thing. These two things can be of the same type or different types. The idiom "follow the gourd and draw the gourd", the gourd here is the prototype, and the gourd is the type. Using JavaScript's prototype to represent it is "ladle.prototype = a certain gourd" or "ladle.prototype = new gourd ()".
To understand prototype in depth, you can study one of its design patterns - prototype pattern. The core of this pattern is to use prototype instances to specify the types of objects to be created, and to create new objects by copying these prototypes. JavaScript prototype is similar to this method.
For details about prototype patterns, please refer to "Design Patterns". It is beyond the scope of this article.
Note that, unlike the relationship between types and instances, the relationship between prototypes and types requires that a type can only have one prototype at a time (and an instance can obviously have multiple types at a time). For JavaScript, this restriction has two meanings. The first is that each specific JavaScript type has and has only one prototype. By default, this prototype is an Object object (note that it is not an Object type!) . The second is that the type to which this object belongs must be a type chain that satisfies the prototype relationship. For example, the types of p1 are Point and Object, and an Object object is the prototype of Point. If there is an object whose types are ClassA, ClassB, ClassC and Object, then these four classes must form a complete prototype chain.
Interestingly, JavaScript does not specify the type of the prototype of a type (this is another very awkward statement), so it can be any type, usually some kind of object, so, object-type-prototype (object) It may form a ring structure, or other interesting topological structures. These structures bring a variety of uses to JavaScript, some of which are not only clever but also full of beauty. The following section mainly introduces the usage of prototype.

2 Tips on using prototype
Before understanding the tips on using prototype, you must first understand the characteristics of prototype. First of all, JavaScript provides a prototype attribute for each type (Type). Point this attribute to an object, and this object becomes the "prototype" of this type. This means that all objects created by this type have this Characteristics of the prototype. In addition, JavaScript objects are dynamic, and prototypes are no exception. Adding or subtracting attributes to prototype will change the prototype of this type. This change will directly affect all objects created by this prototype, for example:

Copy code The code is as follows:

<script> <br>function Point(x,y) <br>{ <br>this.x = x; <br>this.y = y; <br>} <br>var p1 = new Point(1,2); <br>var p2 = new Point(3,4 ; All objects created <br></script>


If an attribute named a is added to the prototype of an object type, and the object itself has an attribute with the same name named a, then when accessing the attribute a of the object, the attribute of the object itself The prototype attribute is "overwritten", but the prototype attribute does not disappear. When you use the delete operator to delete attribute a of the object itself, the object's prototype attribute regains visibility. Using this feature, you can set default values ​​for the properties of the object, for example:
Copy code The code is as follows:

<script> <br>function Point(x, y) <br>{ <br>if(x) this.x = x; <br>if(y) this.y = y; <br>} <br>Point.prototype.x = 0; <br>Point.prototype.y = 0; <br>var p1 = new Point; <br>var p2 = new Point(1,2); <br> </script>

The above example sets the default value (0,0) for the Point object through prototype, so the value of p1 is (0,0) and the value of p2 is (1 ,2), the value of p2 can be restored to (0,0) by delete p2.x, delete p2.y;. Here is a more interesting example:
Copy code The code is as follows:

<script> <br>function classA() <br>{ <br>this.a = 100; <br>this.b = 200; <br>this.c = 300; <br><br>this.reset = function( ) <br>{ <br>for(var each in this) <br>{ <br>delete this[each]; <br>} <br>} <br>} <br>classA.prototype = new classA( ); <br><br>var a = new classA(); <br>alert(a.a); <br>a.a *= 2; <br>a.b *= 2; <br>a.c *= 2; <br>alert(a.a); <br>alert(a.b); <br>alert(a.c); <br>a.reset(); //Call the reset method to restore the value of a to the default value<br>alert(a.a ); <br>alert(a.b); <br>alert(a.c); <br></script> This prevents it from being overwritten. Here is an example:


Copy code The code is as follows: <script> </div> function Point(x, y) <div class="codebody" id="code88210">{ <br>if(x) this.x = x; <br>if(y) this.y = y; <br>} <br>Point.prototype.x = 0; <br>Point.prototype.y = 0; <br><br>function LineSegment(p1, p2) <br>{ <br>//Private member<br>var m_firstPoint = p1; <br>var m_lastPoint = p2; <br>var m_width = { <br>valueOf : function(){return Math.abs(p1.x - p2.x)}, <br>toString : function(){return Math.abs(p1. x - p2.x)} <br>} <br>var m_height = { <br>valueOf : function(){return Math.abs(p1.y - p2.y)}, <br>toString : function() {return Math.abs(p1.y - p2.y)} <br>} <br>//getter <br>this.getFirstPoint = function() <br>{ <br>return m_firstPoint; <br>} <br>this.getLastPoint = function() <br>{ <br>return m_lastPoint; <br>} <br><br>this.length = { <br>valueOf : function(){return Math.sqrt(m_width* m_width m_height*m_height)}, <br>toString : function(){return Math.sqrt(m_width*m_width m_height*m_height)} <br>} <br>} <br>var p1 = new Point; <br>var p2 = new Point(2,3); <br>var line1 = new LineSegment(p1, p2); <br>var lp = line1.getFirstPoint(); <br>lp.x = 100; //Accidental rewriting The value of lp is destroyed, and the original value of lp is destroyed and cannot be recovered <br>alert(line1.getFirstPoint().x); <br>alert(line1.length); //Even line1.lenght has changed<br></script>


Rewrite this.getFirstPoint() as follows:


Copy code The code is as follows: this.getFirstPoint = function()
{
function GETTER(){};
GETTER.prototype = m_firstPoint;
return new GETTER();
}


can avoid this problem and ensure the read-only nature of the m_firstPoint attribute.


Copy code The code is as follows:

<script> <br>function Point(x, y) <br>{ <br>if(x) this.x = x; <br>if(y) this.y = y; <br>} <br>Point.prototype.x = 0; <br>Point.prototype.y = 0; <br><br>function LineSegment(p1, p2) <br>{ <br>//私有成员 <br>var m_firstPoint = p1; <br>var m_lastPoint = p2; <br>var m_width = { <br>valueOf : function(){return Math.abs(p1.x - p2.x)}, <br>toString : function(){return Math.abs(p1.x - p2.x)} <br>} <br>var m_height = { <br>valueOf : function(){return Math.abs(p1.y - p2.y)}, <br>toString : function(){return Math.abs(p1.y - p2.y)} <br>} <br>//getter <br>this.getFirstPoint = function() <br>{ <br>function GETTER(){}; <br>GETTER.prototype = m_firstPoint; <br>return new GETTER(); <br>} <br>this.getLastPoint = function() <br>{ <br>function GETTER(){}; <br>GETTER.prototype = m_lastPoint; <br>return new GETTER(); <br>} <br><br>this.length = { <br>valueOf : function(){return Math.sqrt(m_width*m_width m_height*m_height)}, <br>toString : function(){return Math.sqrt(m_width*m_width m_height*m_height)} <br>} <br>} <br>var p1 = new Point; <br>var p2 = new Point(2,3); <br>var line1 = new LineSegment(p1, p2); <br>var lp = line1.getFirstPoint(); <br>lp.x = 100; //不小心改写了lp的值,但是没有破坏原始的值 <br>alert(line1.getFirstPoint().x); <br>alert(line1.length); //line1.lenght不发生改变 <br></script>

实际上,将一个对象设置为一个类型的原型,相当于通过实例化这个类型,为对象建立只读副本,在任何时候对副本进行改变,都不会影响到原始对象,而对原始对象进行改变,则会影响到副本,除非被改变的属性已经被副本自己的同名属性覆盖。用delete操作将对象自己的同名属性删除,则可以恢复原型属性的可见性。下面再举一个例子:
复制代码 代码如下:

<script> <br>function Polygon() <br>{ <br>var m_points = []; <br><br>m_points = Array.apply(m_points, arguments); <br><br>function GETTER(){}; <br>GETTER.prototype = m_points[0]; <br>this.firstPoint = new GETTER(); <br><br>this.length = { <br>valueOf : function(){return m_points.length}, <br>toString : function(){return m_points.length} <br>} <br><br>this.add = function(){ <br>m_points.push.apply(m_points, arguments); <br>} <br><br>this.getPoint = function(idx) <br>{ <br>return m_points[idx]; <br>} <br><br>this.setPoint = function(idx, point) <br>{ <br>if(m_points[idx] == null) <br>{ <br>m_points[idx] = point; <br>} <br>else <br>{ <br>m_points[idx].x = point.x; <br>m_points[idx].y = point.y; <br>} <br>} <br>} <br>var p = new Polygon({x:1, y:2},{x:2, y:4},{x:2, y:6}); <br>alert(p.length); <br>alert(p.firstPoint.x); <br>alert(p.firstPoint.y); <br>p.firstPoint.x = 100; //不小心写了它的值 <br>alert(p.getPoint(0).x); //不会影响到实际的私有成员 <br>delete p.firstPoint.x; //恢复 <br>alert(p.firstPoint.x); <br><br>p.setPoint(0, {x:3,y:4}); //通过setter改写了实际的私有成员 <br>alert(p.firstPoint.x); //getter的值发生了改变 <br>alert(p.getPoint(0).x); <br></script>

注意,以上的例子说明了用prototype可以快速创建对象的多个副本,一般情况下,利用prototype来大量的创建复杂对象,要比用其他任何方法来copy对象快得多。注意到,用一个对象为原型,来创建大量的新对象,这正是prototype pattern的本质。
下面是一个例子:
复制代码 代码如下:

<script> <br>var p1 = new Point(1,2); <br>var points = []; <br>var PointPrototype = function(){}; <br>PointPrototype.prototype = p1; <br>for(var i = 0; i < 10000; i ) <br>{ <br>points[i] = new PointPrototype(); <br>//由于PointPrototype的构造函数是空函数,因此它的构造要比直接构造//p1副本快得多。 <br>} <br></script>

In addition to the above-mentioned usage techniques, prototype also has other uses because of its unique characteristics. The most widely used and well-known one is probably to use it to simulate inheritance. About this, we will leave it to the next discussed in one section.
3 The essence of prototype
The role of prototype has been mentioned above. Now we will reveal the essence of prototype through rules.
We say that prototype behaves like a static field in C. If you add a property as a property of prototype, this property will be shared by all instances created of this type, but this sharing is read-only. In any instance, you can only overwrite this property with your own property of the same name, but not change it. In other words, when an object reads a certain attribute, it always checks the attribute table of its own domain first. If there is such an attribute, it will return this attribute. Otherwise, it will read the prototype domain and return the attributes on the prototype domain. In addition, JavaScript allows the prototype field to refer to any type of object. Therefore, if the attribute is not found after reading the prototype field, JavaScript will recursively search the prototype field of the object pointed to by the prototype field until the prototype field of the object is it. itself or until a cycle occurs, we can use the following diagram to describe the relationship between prototype and object instance:
//TODO:
4 The value and limitations of prototype
From From the above analysis, we understand prototype, through which we can use an object as a prototype to safely create a large number of instances. This is the true meaning of prototype and its value. We will see later that using this feature of prototype can be used to simulate the inheritance of objects, but you must know that although prototype is used to simulate inheritance, although it is also an important value of it, it is definitely not its core. In other words, JavaScript Therefore, supporting prototype is definitely not just used to implement its object inheritance. Even without prototype inheritance, JavaScript's prototype mechanism is still very useful.
Since prototype only uses objects as prototypes to build copies of types, it also has great limitations. First of all, it does not behave as a value copy in the prototype field of the type, but as a reference copy, which brings "side effects". Changing the property value of a reference type's property on a prototype (another rather awkward explanation :P) will completely affect every instance created of this type. Sometimes this is exactly what we need (such as changing the default value of all objects of a certain class), but sometimes this is also what we don't want (such as when class inheritance). An example is given below:
Copy code The code is as follows:

<script> <br>function ClassA() <br>{ <br>this.a=[]; <br>} <br>function ClassB() <br>{ <br>this.b=function(){}; <br>} <br>ClassB.prototype =new ClassA(); <br>var objB1=new ClassB(); <br>var objB2=new ClassB(); <br>objB1.a.push(1,2,3); <br>alert(objB2 .a); <br>//The members of a in all instances of b have changed! ! This is not what you want in this example. <br></script>

JavaScript implementation:
In the Java language, objects all inherit from java.lang.Object, and java.lang.Object provides the Clone method. As long as the interface Cloneable is implemented, it means support Clone, otherwise an exception is thrown. JavaScript is very close at this point. All objects inherit from Object. However, Object does not support the Clone method. However, we can implement the Clone method through JavaScript in the form of expanddo, so that all object creation in the future can be realized. Clone method.
Because JavaScript itself does not provide a Clone method. At the same time, for object assignments such as var a=new Object(); var b=a, such codes a and b point to the same object. To create an object, you must pass new This keyword is used to implement, so during the implementation process of Clone, I internally defined a constructor (constructor) CloneModel, and also designated its parent object as the object to be used for the Clone activity itself, so the this keyword was used. We create objects one by one based on the constructor CloneModel. Because there is no code inside the constructor, the newly created object actually says that all implementations are in the parent object, which is the object we need to clone. So far, we have created an object that needs to be copied, but all values ​​are pointed to the parent object.
In the object-oriented approach of JavaScript, we have discussed that if the value of the parent object is not overwritten, then it points directly to the parent object at this time. The Prototype Pattern requires that the internal value of the object after Clone should not be relevant. , and as long as it 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 own memory by overwriting it.

21.2.1 What is prototype
The prototype property of an object in JavaScript can return a reference to the prototype of the object type. This is a rather confusing explanation. To understand it, you must first correctly understand the concepts of object type (Type) and prototype (prototype). We said earlier that there is a "creation" relationship between an object's class (Class) and an object instance (Instance). Therefore, we regard "class" as the modeling of object characteristics, and the object as the concrete representation of class characteristics. ization, or in other words, a class (Class) is a type (Type) of an object. For example, in the previous example, the types of p1 and p2 are both Point. In JavaScript, this can be verified through 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 Object is also their type. Because Object is a more general class than Point, we say that there is a derivative relationship between Object and Point. We will know later that this relationship is called "inheritance" ", it is also a special case of the generalized relationship between objects, and is an indispensable basic relationship in object-oriented. In the object-oriented field, instance and type are not the only pair of describable abstract relationships. In JavaScript, another important abstract relationship is type (Type) and prototype (prototype). This relationship is a higher-level abstract relationship, which happens to form a three-layer chain with the abstract relationship between types and instances.

Figure 21.2 describes this relationship:
, The relationship between type and prototype
In real life, we often say that something is created based on another thing. These two things can be of the same type or different types. In the idiom "picture a cat and draw a tiger", the cat here is the prototype, and the tiger is the type. Using JavaScript's prototype to represent it is "tiger.prototype = a certain cat" or "tiger.prototype = new cat()". "Prototype" is a type of "categorization" relationship that describes things in nature. Other relationships include "inheritance" and "interface". Generally speaking, "inheritance" describes the inherent derivative relationship between things, and the things that can be described by "inheritance" have a strong correlation (blood relationship). "Interface" describes the common characteristics of things in terms of functionality. "Prototype" tends to describe the "similarity" between things. From this point of view, "prototype" is broader than inheritance and interfaces in describing the correlation of things. If you are a Java programmer, consider the above example from the perspective of inheritance. Of course, it is impossible to use "cat" to inherit "tiger", and it is also impossible to use "tiger" to inherit "cat". To describe their relationship, you need Create an "abstract class" that covers their commonalities, or you would call it "feline". However, if I only need to use "cat" and "tiger" in my system, then this redundant "cat" has no meaning to me. All I need to express is that "tiger" is a bit like "cat" ", that's all. Here, using prototypes helped us successfully save a type "feline" that was not necessary to create.프로토타입을 심도 있게 이해하기 위해서는 디자인 패턴 중 하나인 프로토타입 패턴을 연구하면 됩니다. 이 패턴의 핵심은 프로토타입 인스턴스를 사용하여 생성할 객체의 유형을 지정하고, 이러한 프로토타입을 복사하여 새로운 객체를 생성하는 것입니다. JavaScript의 프로토타입은 이와 유사합니다. 프로토타입 패턴에 대한 자세한 내용은 이 책의 범위를 벗어나는 "디자인 패턴"을 참조하세요. 프로토타입 패턴에서는 유형이 한 번에 하나의 프로토타입만 가질 수 있어야 합니다(그리고 인스턴스는 분명히 한 번에 여러 유형을 가질 수 있습니다). JavaScript의 경우 이 제한에는 두 가지 의미가 있습니다. 첫 번째는 각 특정 JavaScript 유형에 하나의 프로토타입만 있다는 것입니다. 기본적으로 프로토타입은 Object 객체입니다(Object 유형이 아닙니다!). 두 번째는 이 유형의 모든 인스턴스 유형이 프로토타입 관계를 충족하는 유형 체인이어야 한다는 것입니다. 예를 들어 p1의 유형은 Point와 Object이고 Object 객체는 Point의 프로토타입입니다. 유형이 ClassA, ClassB, ClassC 및 Object인 객체가 있는 경우 이 네 가지 클래스는 완전한 프로토타입 체인을 형성해야 합니다. 예:
예 21.4 프로토타입 관계의 유형 체인
함수 ClassA()
{
……
}
ClassA.prototype = new Object(); //생략 가능
function ClassB()
{
……
}
ClassB.prototype = new ClassA(); //ClassB는 ClassA의 객체를 프로토타입으로 사용합니다.
function ClassC()
{
  …
}
ClassC.prototype = new ClassB( ); //ClassC는 ClassB의 객체를 프로토타입으로 사용합니다.
var obj = new ClassC();
alert(obj instanceof ClassC) //true
alert(obj instanceof ClassB);
alert(objinstanceofClassA); //true
alert(objinstanceofObject); //true

그림 21.3은 이들 간의 관계를 간략하게 설명합니다.
그림 21.3 프로토타입의 유형 체인 관계
흥미롭게도 JavaScript는 유형의 프로토타입의 유형을 지정하지 않으므로(이것은 또 다른 매우 어색한 진술입니다) 어떤 유형이든, 일반적으로 일종의 객체가 될 수 있으므로 object-type -프로토타입(객체 )은 링 구조 또는 기타 흥미로운 토폴로지 구조를 형성할 수 있으며 이러한 구조는 JavaScript에 다양한 용도를 제공하며 그 중 일부는 영리할 뿐만 아니라 아름다움도 가득합니다. 다음 섹션에서는 주로 프로토타입의 사용법을 소개합니다. <
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn