Basic form
Let's take a look at the following code:
The code is as follows |
Copy Code |
constructor function function Shape () {this.x = 0; this.y = 0;} A shape instance var s = new Shape (); |
Although this example is very simple, there are four "very important" points that need to be clarified here:
1.s is an object, and by default it has access to Shape.prototype (that is, each of the prototypes that are created by the shape constructor); In short, Shape.prototype is an object that "watches" all the shape instances. You can think of an object's prototype as a fallback set of many attributes (variables/functions) that will look in the prototype when it cannot find something on its own.
2. Prototypes can be shared across all shape instances. For example, all prototypes have permission to access the prototype (directly).
3. When you call a function in the instance, the instance looks up the definition of the function on its own. If it is not found, the prototype will look for the definition of the function.
4. Regardless of where the definition of the called function is found (in the instance itself or in its prototype), this value is pointing to this instance of the function being invoked. So if we call a function that is dry in s, if the function is not directly defined in S, but in the prototype of S, this value still points to S.
Now we will apply the points highlighted above to an example. Let's say we bind a function getposition () to S. We might have done this:
S.getposition () {return [THIS.X,THIS.Y];} There is nothing wrong with doing this. You can call S.getposition directly () then you will get the returned array.
But what if we create an instance of another shape S2, and can it still invoke the GetPosition () function?
The answer is clearly not.
The GetPosition function is created directly north of the instance S. Therefore, this function does not appear in the S2.
When you call S2.getposition (), the following steps occur sequentially (note that the third step is important):
1. Example S2 will check the definition of getposition; 2. This function does not exist in S2; the 3.S2 prototype (the fallback set shared with s) examines the definition of getposition; 4. This function does not exist in the prototype; 5. The definition of this function is not found;
A simple (but not optimal) solution is to redefine getposition in instance S2 (and every subsequent instance that requires GetPosition). This is a bad practice because you're working on meaningless copy code, and defining a function in each instance consumes more memory (if you care about that).
We have a better idea.
Defining attributes in Prototypes
We can fully achieve the purpose of sharing getposition functions for all instances, not defining getposition in each instance, but in the prototype of constructor functions. Let's look at the following code:
The code is as follows |
Copy Code |
constructor function function Shape () {this.x = 0; this.y = 0;} Shape.prototype.getPosition = function () {return [THIS.X,THIS.Y]; } var s = new shape (), S2 = new shape (); |
Since prototypes are shared in all instances of shape, s and S2 are able to access the GetPosition function.
Calling the S2.getposition () function goes through the following steps:
1. Example s2 Check the definition of getposition; 2. function does not exist in the S2; 3. Examine the prototype; the definition of 4.getPosition is present in the prototype, and 5.getPosition will be executed along with the this point to S2;
Attributes that are bound to a prototype are ideal for reuse. You can reuse the same function in all instances.
Traps in the prototype
Be very careful when you bind objects or arrays into prototypes. All instances will share references to these bound objects/arrays. If an instance manipulates an object or an array, all instances are affected.
The code is as follows |
Copy Code |
function Shape () {this.x = 0; this.y = 0;} Shape.prototype.types = [' round ', ' flat ']; s = new Shape (); S2 = new Shape (); S.types.push (' bumpy '); Console.log (s.types); [' Round ', ' flat ', ' bumpy '] console.log (s2.types); [' Round ', ' flat ', ' bumpy '] |
When the S.types.push (' bumpy ') line of code runs, instance s examines an array called types. It does not exist with the instance s, so the prototype examines the array. This array, types, exists in the prototype, so we add an element ' bumpy ' to him.
As a result, because S2 also shares prototypes, it can also find that types arrays have changed in a way that is not straightforward.
Similar things happen in the real world when you use Backbone.js. When you define a view/model/set, backbone adds the attributes you pass through the Extend function (for example, Backbone.View.extend ({})) to the prototype of the entity you define.
This means that if you add an object or an array to the definition of an entity, all instances will share those objects or arrays, and it is likely that one of your instances will destroy another instance. To avoid this, you often see dreams include these objects/arrays in a function, returning an instance of an object/array at a time:
Note: Backbone this in the section of model defaults:
Remember that in JavaScript, objects are passed in a reference way, so if you include an object as the default, it will be shared in all instances. So, we define defaults as a function. Another type of shape
Suppose now we want to create a particular type of shape, such as a circle. It would be nice if it could inherit all of the features of shape and also define custom functions in its prototype:
The code is as follows |
Copy Code |
function Shape () {this.x = 0; this.y = 0;} function Circle () {This.radius = 0;} |
So how do we describe a circle as a shape? There are several ways to do this:
1. Borrow constructor and assign value to prototype
When we create a circle, we want the instance to have a radius (from the circle constructor), and an x position, a Y position (from the shape constructor).
We simply declare C = new Circle (), then C only has a radius. The shape constructor initializes x and Y. We want this function. So we're going to use this function.
The code is as follows |
Copy Code |
function Circle () {This.radius = 0; Shape.call (this); } |
The last line of code Shape.call (this) invokes the shape constructor and changes the this value to point to this when the circle constructor is invoked. What are you talking about?
Now let's use the constructor above to create a new circle and see what happens:
The code is as follows |
Copy Code |
var c = new Circle ();
|
This line of code calls the Circle constructor, which first bound a variable radius in C. Remember, this is the point of C. We then call the shape constructor, and then point the this value in shape to the current value in circle, which is C. The shape constructor binds x and Y to the current this, that is, C now has the X and Y properties with a value of 0.
In addition, it is not important that you place Shape.call (this) in this example. If you want to overload x and Y after initialization (that is, place the center in a different location), you can do it after you call the shape function.
The problem is that now our instantiated circle has variable x,y and radius, but it doesn't get anything from the prototype of shape. We need to set the circle constructor to reuse the shape's prototype as its prototype-so that all circles can gain the benefit of shape.
One way is to set the value of Circle.prototype to Shape.prototype:
The code is as follows |
Copy Code |
function Shape () {this.x = 0; this.y = 0;} Shape.prototype.getPosition = function () {return [this.x, This.y]; function Circle () {This.radius = 0; Shape.call (this); } Circle.prototype = Shape.prototype; var s = new Shape (), C = new Circle (); |
This works fine, but it's not the best choice. Instance c now has permission to access the GetPosition function because the Circle constructor function and the shape constructor function share its prototype.
What if we still want to define a Getarea function for all the elements? We will bind this function to the prototype of the Circle constructor function so that it can be used for all circles.
Write the following code:
The code is as follows |
Copy Code |
function Shape () {this.x = 0; this.y = 0;} Shape.prototype.getPosition = function () {return [this.x, This.y]; function Circle () {This.radius = 0; Shape.call (this); } Circle.prototype = Shape.prototype; Circle.prototype.getArea = function () {return Math.PI * This.radius * this.radius;}; var s = new Shape (), C = new Circle (); |
The situation now is that circle and shape share the same prototype, and we add a function to the circle.prototype that is actually equivalent to adding a function to the shape.prototype.
How could it be this way!
An instance of shape does not have a RADIUS variable, only the circle instance has a RADIUS variable. But now, all of the shape instances can access the Getarea function – which results in an error, but everything is fine when all the circles call this function.
Setting all the prototypes to the same object does not meet our needs.
The 2.Circle prototype is an example of a shape
The code is as follows |
Copy Code |
function Shape () {this.x = 0; this.y = 0;} Shape.prototype.getPosition = function () {return [this.x, This.y]; function Circle () {This.radius = 0;} Circle.prototype = new Shape (); var c = new Circle (); |
This method is very cool. We have not borrowed constructor functions but circle has x and Y, and also has the GetPosition function. How does it come true?
Circle.prototype is now an example of a shape. This means that C has a direct variable radius (provided by the Circle constructor function). However, in the prototype of C, there is an x and a Y. Now notice that the interesting thing is coming up: in the prototype of C, there is a definition of the getposition function. It seems to be like this:
Enter image description Here so if you are trying to get c.x, then it will be found in the prototype C.
The disadvantage of this approach is that if you want to overload x and Y, you have to do this in the circle constructor or the circle prototype.
The code is as follows |
Copy Code |
function Shape () {this.x = 0; this.y = 0;} Shape.prototype.getPosition = function () {return [this.x, This.y]; function Circle () {This.radius = 0;} Circle.prototype = new Shape (); circle.prototype.x = 5; Circle.prototype.y = 10; var c = new Circle (); Console.log (C.getposition ()); [5, 10] |
Calling C.getposition will go through the following steps:
1. The function is not found in C; 2. The function was not found in the prototype of C (instance of shape), 3. The function is found in the prototype of the shape instance (prototype of C), 4. This function is called together with this point C, 5. In the definition of the GetPosition function, we look for x in this, 6.x is not found directly in C; 7. We look for X in C's prototype (shape instance), and we find X in the prototype C, 9. We find y in the prototype of C;
Apart from the headaches of a layer of prototype chain, this method is good.
This method can also be replaced with object.create ().
3. Borrow the constructor and use the Object.create
The code is as follows |
Copy Code |
function Shape () {this.x = 0; this.y = 0;} Shape.prototype.getPosition = function () {return [this.x, This.y]; function Circle () {This.radius = 0; Shape.call (this); This.x = 5; This.y = 10; } Circle.prototype = Object.create (Shape.prototype); var c = new Circle (); Console.log (C.getposition ()); [5, 10] |
One of the great benefits of this approach is that X and Y are directly bound to C – which makes the query much faster (if your program cares about this) because you don't need to query the prototype chain anymore.
Let's take a look at the object.create alternative (Polyfill):
The code is as follows |
Copy Code |
Object.create = (function () {//Intermediate constructor function F () {} |
return function (o) {...////Set the prototype of the intermediate constructor to the object we gave it o f.prototype = o;//Returns an instance of an intermediate constructor;//It is an empty object but the prototype is the object we gave it o return new F () ; }; })();
The process basically completes the Circle.prototype = new Shape (); just now Circle.prototype is an empty object (an instance of an intermediate constructor F), and its prototype is shape.prototype.
Which method should you use
It is important to remember that if you bind an object/array on the shape constructor, then all the circles can modify the shared objects/arrays. This method can be very flawed if you set Circle.prototype to an instance of shape.
The code is as follows |
Copy Code |
function Shape () {this.x = 0; this.y = 0; this.types = [' flat ', ' round '];} Shape.prototype.getPosition = function () {return [this.x, This.y]; function Circle () {This.radius = 0;} Circle.prototype = new Shape (); var c = new Circle (), C2 = new Circle (); C.types.push (' bumpy '); Console.log (c.types); ["Flat", "round", "bumpy"] console.log (c2.types); ["Flat", "round", "bumpy"] |
To avoid this, you can borrow the shape constructor and use object.create so that each circle can have its own types array.
The code is as follows |
Copy Code |
... function Circle () {This.radius = 0; Shape.call (this); } Circle.prototype = Object.create (Shape.prototype); var c = new Circle (), C2 = new Circle (); C.types.push (' bumpy '); Console.log (c.types); ["Flat", "round", "bumpy"] console.log (c2.types); ["Flat", "round"] |
A more advanced example
We are now going further on the basis of the previous discussion, creating a new circle type, Sphere. An ellipse is about the same as a circle, but there are different formulas for calculating the area.
code is as follows |
copy code |
function Shape () {this.x = 0; this.y = 0;} Shape.prototype.getPosition = function () {return [this.x, This.y]; Function Circle () {This.radius = 0; Shape.call (this); this.x = 5; this.y = ten;} Circle.prototype = object.create (shape.prototype); Circle.prototype.getArea = function () {return Math.PI * This.radius * This.radius; Function Sphere () {} //TODO: Set up a prototype chain here Sphere.prototype.getArea = function () {return 4 * Math.PI * This.radius * This.radius; var sp = new Sphere (); |
Which method should we use to set up the prototype chain? Remember, we don't want to destroy the definition of the getarea of the circle. We just want to have another way of implementing in the ellipse.
We can also borrow the constructor and assign a value to the stereotype (method 1). Because doing so will change the definition of the getarea of all circles. However, we can use object.create or set the sphere prototype to an instance of circle. Let's see what we can do:
The code is as follows |
Copy Code |
... function Circle () {This.radius = 0; Shape.call (this); This.x = 5; This.y = 10; } Circle.prototype = Object.create (Shape.prototype); Circle.prototype.getArea = function () {return Math.PI * This.radius * this.radius;}; function Sphere () {circle.call (this);} Sphere.prototype = Object.create (Circle.prototype); Sphere.prototype.getArea = function () {return 4 * Math.PI * This.radius * this.radius;}; var sp = new Sphere (); |
Calling Sp.getarea () will go through the steps:
1. Find the definition of Getarea in SP 2. The definition was not found in the SP, 3. In the Sphere prototype (an intermediate object, its prototype is Circle.prototype), 4. Find the definition of Getarea in this intermediate object, Since we have redefined Getarea in the prototype of sphere, here is a new definition; 5. With this call to SP Getarea method;
We note that Circle.prototype also has a getarea definition. However, since Sphere.prototype already has a getarea definition, we will never use the getarea– in Circle.prototype so that we can successfully "overload" This function (overload one, which defines a function with the same name in front of the query chain).