1. Preface
This article is translated from Microsoft's great Scott Allen Prototypes and Inheritance in JavaScript. This article has made a detailed analysis and explanation of what Prototype is and why it can be inherited through Prototype. It is one of the best works for understanding JS OO. I hope you can modify and add some of the poor translations.
2. Text
Object-oriented in JavaScript is different from other languages. It is best to forget the concept of object-oriented you are familiar with before learning. OO in JS is more powerful, more arguably and more flexible.
1. Classes and Objects
JS is an object-oriented language from a traditional point of view. The properties and behaviors are combined into one object. For example, an array in JS is an object composed of attributes and methods (such as push, reverse, pop, etc.).
var myArray = [1, 2];
(3);
();
();
var length = ;
The question is: Where did these methods (such as push) come from? Some static languages (such as JAVA) use class to define the structure of an object. However, JS does not have a "class" (classless) language, and no class called "Array" defines these methods for each array to inherit. Because JS is dynamic, we can add methods to the object at will when we need it. For example, the following code defines an object that represents coordinates in two-dimensional space with an add method inside.
var point = {
x : 10,
y : 5,
add: function(otherPoint)
{
= ;
= ;
}
};
We want each point object to have an add method. We also want all point objects to share an add method without adding the add method to all point objects. This requires prototype to appear.
2. About Prototypes
Each object in JS has an implicit property (state) - a reference to another object, called the object's prototype. The array and point we created above also contain references to their respective prototypes. The prototype reference is implicit, but it is implemented by ECMAScript, allowing us to get it using the object's _proto_ (in Chrome) property. From a conceptual perspective, we can think that the relationship between an object and a prototype is as shown in the figure below:
(point) != (myArray);
In the next part of the article, I will also use _proto_, mainly because _proto_ is more intuitive in the illustrations and sentences. But remember that this is not standardized, it is the recommended method to obtain the object prototype.
2.1 What makes Prototypes so special?
We already know that the push method of array comes from myArray's prototype object. Figure 2 is a screenshot in Chrome. We call the method to obtain the prototype object of myArray.
Notice that myArray's prototype object contains many methods, such as push, pop and reverse, which we have used in the beginning code. The prototype object is the only owner of the push method, but how is this method called through myArray?
(3);
To understand how it is implemented, the first step is to realize that Protype is not special at all. Prototype is some objects. We can add methods and properties to these objects, like any other JS object. But at the same time, Prototype is also a special object.
The specialty of Prototype is because of the following rules: When we notify JS that we want to call the push method or read a certain property on an object, the interpreter (runtime) first looks for the method or property of the object itself. If the interpreter does not find the method (or property) it will follow the _proto_ reference to find each member of the object's prototype. When we call the push method in myArray, JS does not find push in myArray object, but in myArray's prototype object, that is, the push method is called (Figure 3).
The behavior I'm describing is essentially that the object itself inherits all methods and properties in its prototype. We do not need to use class to implement this inheritance relationship in JS. That is, a JS object inherits features from its prototype.
Figure 3 also tells us that each array object can maintain its own state and membership. If we need the length property of myArray, JS will find the length value from myArray without searching in prototype. We can use this feature to "override" a method, that is, put the method to be overridden (like push) into myArray's own object. This can effectively hide the push method in prototype.
3. Share Prototype
The real magic of Prototype in JS is that multiple objects can refer to the same prototype object. For example, we create two arrays:
var myArray = [1, 2];var yourArray = [4, 5, 6];
These two arrays will share an identical prototype object, and the following code will return true
(myArray) === (yourArray);
If we call the push method in two arrays, JS will call push in their common prototype.
Next you may ask: How do I set the prototype reference of a custom object? For example, for the object point we created before, how can we add an add method to the prototype object so that all point objects can inherit it? Before we answer this question, let's first understand the functions in JS.
4. About Function
Functions are also objects in JS. Functions have many important features in JS, and we cannot list them one by one in this article. But it is a very basic way to assign a function to a variable or treat a function as a parameter to another function in today's JS programming.
What we need to pay attention to is: because a function is an object, it has a reference to a method, property and a prototype object. Let's discuss the meaning of the following code:
// this will return true:
typeof (Array) === "function"
// and so will this:
(Array) === (function () { })
// and this, too:
!= null
The first line of code proves that Array is a function in JS. We will see how to call the Array function to create a new array object later.
The second line of code proves that the Array object and the function object refer to the same prototype, just like all array objects we saw before share a prototype.
The last line proves that the Array function has a prototype property. Never confuse this prototype attribute with the _proto_ attribute. Their purpose and the object they are pointing to are different.
// true
== (myArray)
// also true
== (yourArray);
We use the newly learned knowledge to repaint the previous pictures:
// create a new, empty object
var o = {};
// inherit from the same prototype as an array object
o.__proto__ = ;
// now we can invoke any of the array methods ...
(3);
Although the above code looks good, the question is whether every JS environment supports the object's _proto_ attribute. Fortunately, JS has a standard mechanism built-in to create new objects and set the object's _proto_ attribute, which is the "new" operator.
var o = new Array();
(3);
The "new" operator has three important tasks in JS: First, it creates a new empty object. Next, it sets the _proto_ attribute of this new object to point to the prototype attribute of the calling function. Finally, execute the calling function and point the "this" pointer to the new object. If we expand the above two lines of code, we will get the following code:
var o = {};
o.__proto__ = ;
(o);
(3);
The "call" method of a function allows you to call a function and specify that the "this" in this function points to the new object passed. Of course, we also want to create our own objects through the above method to realize object inheritance. This kind of function is what we are familiar with - constructors.
5. Constructor
A constructor is a normal JS function object with two unique identifiers:
1. The initial letter is capitalized (easy to recognize).
2. Use the new operator to connect to construct a new object.
Array is a constructor - the Array function is connected with new and has a capital letter. The Array function in JS is built-in, but anyone can create their own constructor. In fact, it's finally time for us to create a constructor for the point object.
var Point = function (x, y) {
= x;
= y;
= function (otherPoint) {
= ;
= ;
}
}
var p1 = new Point(3, 4);
var p2 = new Point(8, 6);
(p2);
In the above code, we use the new operator and the Point function to construct a point object. In memory, you can think of the final result as shown in Figure 6.
The problem now is that the add method exists in every point object. Given our understanding of prototype, adding the add method to it is a better choice (no need to copy the code of the add method to each object). To achieve this, we need to make some modifications to the object.
var Point = function (x, y) {
= x;
= y;
}
= function (otherPoint) {
= ;
= ;
}
var p1 = new Point(3, 4);
var p2 = new Point(8, 6);
(p2);
alright! We have implemented inheritance in JS with prototype!
I hope this article allows you to clear away the fog of prototype. Of course this is just an introduction to the powerful and flexible prototype. For more knowledge about prototype, I still hope readers can explore and discover it on their own.
This article is translated from Microsoft's great Scott Allen Prototypes and Inheritance in JavaScript. This article has made a detailed analysis and explanation of what Prototype is and why it can be inherited through Prototype. It is one of the best works for understanding JS OO. I hope you can modify and add some of the poor translations.
2. Text
Object-oriented in JavaScript is different from other languages. It is best to forget the concept of object-oriented you are familiar with before learning. OO in JS is more powerful, more arguably and more flexible.
1. Classes and Objects
JS is an object-oriented language from a traditional point of view. The properties and behaviors are combined into one object. For example, an array in JS is an object composed of attributes and methods (such as push, reverse, pop, etc.).
Copy the codeThe code is as follows:
var myArray = [1, 2];
(3);
();
();
var length = ;
The question is: Where did these methods (such as push) come from? Some static languages (such as JAVA) use class to define the structure of an object. However, JS does not have a "class" (classless) language, and no class called "Array" defines these methods for each array to inherit. Because JS is dynamic, we can add methods to the object at will when we need it. For example, the following code defines an object that represents coordinates in two-dimensional space with an add method inside.
Copy the codeThe code is as follows:
var point = {
x : 10,
y : 5,
add: function(otherPoint)
{
= ;
= ;
}
};
We want each point object to have an add method. We also want all point objects to share an add method without adding the add method to all point objects. This requires prototype to appear.
2. About Prototypes
Each object in JS has an implicit property (state) - a reference to another object, called the object's prototype. The array and point we created above also contain references to their respective prototypes. The prototype reference is implicit, but it is implemented by ECMAScript, allowing us to get it using the object's _proto_ (in Chrome) property. From a conceptual perspective, we can think that the relationship between an object and a prototype is as shown in the figure below:
As developers, we will use functions instead of the _proto_ attribute to view the prototype reference of the object. When writing this article, this function has been supported in Chrome, firefox, and IE9. There will be more browsers to support this feature in the future, which is already one of the standards of ECMAScript. We can use the following code to prove that myArray and the point object we created earlier do refer to two different prototype objects.
Copy the codeThe code is as follows:
(point) != (myArray);
In the next part of the article, I will also use _proto_, mainly because _proto_ is more intuitive in the illustrations and sentences. But remember that this is not standardized, it is the recommended method to obtain the object prototype.
2.1 What makes Prototypes so special?
We already know that the push method of array comes from myArray's prototype object. Figure 2 is a screenshot in Chrome. We call the method to obtain the prototype object of myArray.
Figure 2
Notice that myArray's prototype object contains many methods, such as push, pop and reverse, which we have used in the beginning code. The prototype object is the only owner of the push method, but how is this method called through myArray?
Copy the codeThe code is as follows:
(3);
To understand how it is implemented, the first step is to realize that Protype is not special at all. Prototype is some objects. We can add methods and properties to these objects, like any other JS object. But at the same time, Prototype is also a special object.
The specialty of Prototype is because of the following rules: When we notify JS that we want to call the push method or read a certain property on an object, the interpreter (runtime) first looks for the method or property of the object itself. If the interpreter does not find the method (or property) it will follow the _proto_ reference to find each member of the object's prototype. When we call the push method in myArray, JS does not find push in myArray object, but in myArray's prototype object, that is, the push method is called (Figure 3).
Figure 3
The behavior I'm describing is essentially that the object itself inherits all methods and properties in its prototype. We do not need to use class to implement this inheritance relationship in JS. That is, a JS object inherits features from its prototype.
Figure 3 also tells us that each array object can maintain its own state and membership. If we need the length property of myArray, JS will find the length value from myArray without searching in prototype. We can use this feature to "override" a method, that is, put the method to be overridden (like push) into myArray's own object. This can effectively hide the push method in prototype.
3. Share Prototype
The real magic of Prototype in JS is that multiple objects can refer to the same prototype object. For example, we create two arrays:
Copy the codeThe code is as follows:
var myArray = [1, 2];var yourArray = [4, 5, 6];
These two arrays will share an identical prototype object, and the following code will return true
Copy the codeThe code is as follows:
(myArray) === (yourArray);
If we call the push method in two arrays, JS will call push in their common prototype.
Prototype objects give us this inherited feature in JS, and they also allow us to share the implementation of methods. Prototype is also chained. In other words, a prototype is an object, and the prototype object can also have a reference to another prototype object. From Figure 2, we can see that the _proto_ attribute of the prototype is a value that is not null and also points to another prototype. When JS starts looking for member variables, such as push method, it will check each object along the references of these prototypes until it is found or reaches the tail of the chain. This chaining method increases the flexibility of inheritance and sharing in JS.
Next you may ask: How do I set the prototype reference of a custom object? For example, for the object point we created before, how can we add an add method to the prototype object so that all point objects can inherit it? Before we answer this question, let's first understand the functions in JS.
4. About Function
Functions are also objects in JS. Functions have many important features in JS, and we cannot list them one by one in this article. But it is a very basic way to assign a function to a variable or treat a function as a parameter to another function in today's JS programming.
What we need to pay attention to is: because a function is an object, it has a reference to a method, property and a prototype object. Let's discuss the meaning of the following code:
Copy the codeThe code is as follows:
// this will return true:
typeof (Array) === "function"
// and so will this:
(Array) === (function () { })
// and this, too:
!= null
The first line of code proves that Array is a function in JS. We will see how to call the Array function to create a new array object later.
The second line of code proves that the Array object and the function object refer to the same prototype, just like all array objects we saw before share a prototype.
The last line proves that the Array function has a prototype property. Never confuse this prototype attribute with the _proto_ attribute. Their purpose and the object they are pointing to are different.
Copy the codeThe code is as follows:
// true
== (myArray)
// also true
== (yourArray);
We use the newly learned knowledge to repaint the previous pictures:
Figure 5
Now we want to create an array object. One of the methods is:
Copy the codeThe code is as follows:
// create a new, empty object
var o = {};
// inherit from the same prototype as an array object
o.__proto__ = ;
// now we can invoke any of the array methods ...
(3);
Although the above code looks good, the question is whether every JS environment supports the object's _proto_ attribute. Fortunately, JS has a standard mechanism built-in to create new objects and set the object's _proto_ attribute, which is the "new" operator.
Copy the codeThe code is as follows:
var o = new Array();
(3);
The "new" operator has three important tasks in JS: First, it creates a new empty object. Next, it sets the _proto_ attribute of this new object to point to the prototype attribute of the calling function. Finally, execute the calling function and point the "this" pointer to the new object. If we expand the above two lines of code, we will get the following code:
Copy the codeThe code is as follows:
var o = {};
o.__proto__ = ;
(o);
(3);
The "call" method of a function allows you to call a function and specify that the "this" in this function points to the new object passed. Of course, we also want to create our own objects through the above method to realize object inheritance. This kind of function is what we are familiar with - constructors.
5. Constructor
A constructor is a normal JS function object with two unique identifiers:
1. The initial letter is capitalized (easy to recognize).
2. Use the new operator to connect to construct a new object.
Array is a constructor - the Array function is connected with new and has a capital letter. The Array function in JS is built-in, but anyone can create their own constructor. In fact, it's finally time for us to create a constructor for the point object.
Copy the codeThe code is as follows:
var Point = function (x, y) {
= x;
= y;
= function (otherPoint) {
= ;
= ;
}
}
var p1 = new Point(3, 4);
var p2 = new Point(8, 6);
(p2);
In the above code, we use the new operator and the Point function to construct a point object. In memory, you can think of the final result as shown in Figure 6.
Figure 6
The problem now is that the add method exists in every point object. Given our understanding of prototype, adding the add method to it is a better choice (no need to copy the code of the add method to each object). To achieve this, we need to make some modifications to the object.
Copy the codeThe code is as follows:
var Point = function (x, y) {
= x;
= y;
}
= function (otherPoint) {
= ;
= ;
}
var p1 = new Point(3, 4);
var p2 = new Point(8, 6);
(p2);
alright! We have implemented inheritance in JS with prototype!
6. Summary
I hope this article allows you to clear away the fog of prototype. Of course this is just an introduction to the powerful and flexible prototype. For more knowledge about prototype, I still hope readers can explore and discover it on their own.