Document directory
This article is also published in another independent blog Javascript: from prototype to inheritance (1)
The prototype chain of Semantic Script has always been a difficult point. This article is a summary of my learning in this period. Here we will not talk about the ECMAScript standard, it will not use UML to draw various relationships (there are many articles in combination with these two aspects, but most of them are rather obscure, such as Uncle Tom). It only strives to be the most simple and easy to understand, for your reference.
Javascript Functions are an object. They have methods and attributes, such as call/apply, while prototype is an attribute of function.
Once you define a function, it comes with a prototype attribute.
function t(){};
typeof t.prototype // "object";
You may already know how to use a function as a constructor to produce a series of objects. For example
Function Some (name, color ){
This. name = name;
This. color = color;
This. method = function (){}
}
Var a1 = new Some ("Lee", "black"); // instantiate an object
The attributes and methods of Some classes above can also be placed in prototype objects, such
Function Some (){}
Some. prototype. name = "Lee" // format 1
Some. prototype = {// Form 2 name: "lee", color: "black", method: function (){}
}
Var a1 = newSome ("Lee", "black"); // instantiate an object
Although the form is different, at least the effect is consistent. When you use a. Lee or a. method, the results are the same.
OK, so the first thing to note is that prototype is the property of live!
function Some(){}
var a =newSome();
a.method // undefined
Some.prototype.method = function(){ console.log("hello");
}a.method // function () {console.log("Hello")}
The code above shows that when instance a is generated, the constructor does not have a method, so neither does a. It is understandable; however, after that, the constructor is added to the prototype attribute. Although the constructor is added after a is generated, a still owns the constructor,It is independent of the time when the constructor is added.
The second problem arises. What if the same field is defined inside the object and prototype? For example:
function Some(){
this.color ="yellow";
}
Some.prototype.color ="black";
var a =newSome();
a.color //?
In the above code, I defined color in both the object's interior and prototype. Which color should I display when I access from an instance?
Note that the javascript engine first checks whether there is color in the properties of a. If not, the prototype (a. constructor. prototype) of its constructor has this attribute.
Let's look at it a little farther. Any object should have its own constructor. The prototype attribute of the function is also an object. What is its constructor?
functionSome(){
this.color ="yellow";
}
var a =newSome();
a.constructor.prototype.constructor // function Some() {this.color = "yellow";}a.constructor.prototype.constructor.prototype // Some {}
The prototype chain above can be traced infinitely,The prototype chain can trace back to the final constructor.Object()This explains why even if we do not definetoString()Function,a.toString()Because it finally calls the toString method of the Object that is traced back.
The new question is, how do you differentiate your own properties from those on the prototype chain, and can you ensure that all properties are accessible?
As we all knowfor...inLoop can solve this problem. You only need to remember three points.
- Although the attributes of the object and the prototype chain are listed in the loop, not all attributes are listed, such as the length and length of an array. splice and other methods are not necessarily listed. All the attributes that can be listed areEnumerable)
- How can we distinguish the attributes of an object from those of the prototype chain? Use
hasOwnProperty()Method
- Note:
propertyIsEnumerable()Method. Although the method name is "enumeration property", all properties in the prototype chain will be rejected by false
Another object is called _ prop __. I personally think it is of little use. It is only recommended for debugging. google is the specific usage.
Prototype inheritance
How do I write a good inheritance method? This is a process of gradual evolution. Let's start with the simplest inheritance.
function Parent(){
this.deep ="Hello";
}
function Child(){
this.shallow ="World";
}
Child.prototype =new Parent();
var c =newChild();
console.log(c.deep);
When we want to access the deep attribute of c
- First, go to the c object to check whether there are any deep attributes. No
- Go to the properties of the c. construct. prototype object and find the Parent instance.
However, the above Code has a problem. When you continuously instantiate Child, the Parent will not be instantiated and will generate a deep load in the memory. If this deep is shared, put deep in prototype.
function Parent(){}
Parent.prototype.deep ="Hello";
function Child(){
this.shallow ="World";
}
Child.prototype =new Parent();
var c =newChild();
console.log(c.deep)
When we want to access the deep attribute of c
- First, go to the c object to check whether there are any deep attributes. No
- Go to the properties of the c. construct. prototype object, and find the Parent instance parent. No
- Go to parent. construct. prototype to find deep,
One of the drawbacks of doing so is that when you look for an attribute, you may find another round.
Let's continue to improve. We found that the deep we need is only on the prototype of the Parent. In fact, I only need the prototype of the Parent, not the Parent instance.
function Parent(){}
Parent.prototype.deep ="Hello";
function Child(){
this.shallow ="World";
}
Child.prototype = Parent.prototype;
var c =new Child();
console.log(c.deep);
This not only avoids Parent instantiation, but also avoids further search in the previous example. However, there is a side effect, because it is a direct reference to the object, so when Child. prototype. deep is modified, Parent. prototype. deep will also be modified. The goal of our further optimization is very clear. We need to block this direct reference to the prototype of the parent class.
So we decided to use an intermediate variable.
Function Parent (){}
Parent. prototype. deep = "Hello"; // Note: var F = function () {}; F. prototype = Parent. prototype;
Function Child (){
This. shallow = "World ";
}
Child. prototype = new F ();
Var c = new Child ();
Console. log (c. deep)
We use F as an intermediate variable to prevent child's modifications to the deep from possibly affecting the parent.
When we want to access the deep attribute of c
- First, go to the c object to check whether there are any deep attributes. No
- Go to the properties of the c. construct. prototype object and find in the instance of F. No
- Go to F. construct. prototype to find the deep,
Let's explain why the modification to Child. prototype does not affect Parent. prototype.
- In the previous example, the Child. prototype operation is the Parent. prototype operation,Whether it's reading or writing, it's using someone else's
- In this example, Child. prototype is not a direct reference to Parent, but a new null object. When we need deep without deep, we are forced to go to the Child. prototype constructor to find it and trace it back to Parent. protoype. When we need to write, what we actually manipulate is
Child.prototype = {}This empty object.
So we abstract the last code snippet into a method.
Function extend (Child, Parent ){
Var F = function (){};
F. prototype = Parent. prototype;
Child. prototype = new F ();
// Once the prototype of the function is reset, You need to assign prototype. constructor a new value,
// Ignore this introduction
Child. prototype. constructor = Child;
// Retain the reference to the parent class,
// Ignore the introduction
Child. uber = Parent. prototype;
}