This article started on Github, welcome issue/FXXK.
preface
The first version of ES6 was released in June 2015, and this article was originally written in 2016, the early days of my front-end work. At the time, many of ES6’s features were still in their stages and not nearly as ubiquitous as they are now, and I spent a full day understanding prototypes — a huge mountain to climb as a mature JavaScript developer — in order to write JavaScript more easily.
ES6 comes with too much syntactic sugar, where the arrow function obscures the magic of this, and class obscures the archetype of this article at length.
Recently, I rewrote this article, through which you will learn:
- How to use
ES5
Simulation class; - understand
prototype
和__proto__
; - Understanding prototype chains and prototype inheritance;
- Learn more
JavaScript
The language.
Introduction: ordinary objects and function objects
In JavaScript, there’s always a saying that everything is an object. In fact, in JavaScript, objects are different, we can divide them into ordinary objects and function objects. Object and Function are two typical Function objects that come with JavaScript. A function object is a pure function, and a function object is a JavaScript simulation of a class.
So, what exactly is an ordinary object and what is a function object? Take a look at the example below:
First, we create three instances of Function and Object respectively:
function fn1() {}
const fn2 = function() {}
const fn3 = new Function('language'.'console.log(language)')
const ob1 = {}
const ob2 = new Object(a)const ob3 = new fn1()
Copy the code
Print the following result to get:
console.log(typeof Object); // function
console.log(typeof Function); // function
console.log(typeof ob1); // object
console.log(typeof ob2); // object
console.log(typeof ob3); // object
console.log(typeof fn1); // function
console.log(typeof fn2); // function
console.log(typeof fn3); // function
Copy the code
In the above example, ob1, ob2, and ob3 are common objects (all instances of Object), while fn1, fn2, and fn3 are all instances of Function, called Function objects.
How do you tell the difference? Actually, just remember this:
- all
Function
Are all examples ofThe function object
“And all the others areOrdinary objects
.
All instances of Function are Function objects. Is Function also an instance of Function?
Save the question for now. Next, to sum up the content of this section:
As you can see from the figure, the implementation of the object itself depends on the constructor. So what exactly is the prototype chain for?
As we all know, as an Object Oriented language, it must have the following characteristics:
- Object uniqueness
- abstract
- inheritance
- polymorphism
And the biggest purpose of the prototype chain is to achieve inheritance.
Advances: Prototype and __proto__
How exactly does a prototype chain implement inheritance? First, we’ll introduce two brothers: Prototype and __proto__. These are two variables that are ubiquitous in JavaScript (if you debug them often). However, these two variables do not exist on all objects.
Object type | prototype |
__proto__ |
---|---|---|
Common Object (NO) | ❎ | ✅ |
Function object (FO) | ✅ | ✅ |
First of all, we draw the following conclusions:
- only
The function object
withprototype
This property; prototype
和__proto__
Are allJavaScript
Created automatically when a function or object is definedPredefined attributes
.
Next, we verify the above two conclusions:
function fn() {}
console.log(typeof fn.__proto__); // function
console.log(typeof fn.prototype); // object
const ob = {}
console.log(typeof ob.__proto__); // function
console.log(typeof ob.prototype); // undefined, wow! Sure enough, the normal object has no prototype
Copy the code
Since they are preset attributes at the language level, what is the difference between the two? We still proceed from the conclusion and draw the following two conclusions:
prototype
By example of__proto__
Point to (passive)__proto__
Object pointing to the constructorprototype
(active)
Wow, that means the following code works:
console.log(fn.__proto__ === Function.prototype); // true
console.log(ob.__proto__ === Object.prototype); // true
Copy the code
If fn is a function object, then what is fn.prototype.__proto__?
Here’s how I tried to solve the problem:
- First of all use
typeof
getfn.prototype
The type of:"object"
- Wow, since it is
"object"
thatfn.prototype
Is it not an instance of Object? Based on the above conclusion, quickly write verification code:
console.log(fn.prototype.__proto__ === Object.prototype) // true
Copy the code
Next, if you were asked to quickly write the JavaScript initialization code for a function prototype when you create it, would you also quickly write:
// The actual code
function fn1() {}
// JavaScript executes automatically
fn1.protptype = {
constructor: fn1,
__proto__: Object.prototype
}
fn1.__proto__ = Function.prototype
Copy the code
Here, do you have a sense of enlightenment? In addition, since normal objects are instantiated by function object instantiation (new), and an instance cannot be instantiated again, another object’s __proto__ will not point to its prototype, So the conclusion that normal objects have no Prototype property at the beginning of this section seems fairly straightforward. From the above analysis, we can also see that fn1. protpType is a normal object, it does not have the protpType attribute.
Going back to the last section, we still have a question:
- Don’t
Function
Is alsoFunction
The instance?
It’s time to get rid of it and make it true. Now, please show me your code!
Look at the answer
console.log(Function.__proto__ === Function.prototype) // true
Copy the code
Key: Prototype chain
In the last section we looked at Prototype and __proto__ in detail. In fact, these two brothers are mainly there to build prototype chains.
Let’s start with the following code:
const Person = function(name, age) {
this.name = name
this.age = age
} / * 1 * /
Person.prototype.getName = function() {
return this.name
} / * 2 * /
Person.prototype.getAge = function() {
return this.age
} / * * / 3
const ulivz = new Person('ulivz'.24); / * * / 4
console.log(ulivz) / * * / 5
console.log(ulivz.getName(), ulivz.getAge()) / * * / 6
Copy the code
Explain the implementation details:
- perform
1
, creates a constructorPerson
Note that, as mentioned earlier, at this timePerson.prototype
Has been automatically created. It containsconstructor
和__proto__
These two properties; - perform
2
To objectPerson.prototype
Added a methodgetName()
; - perform
3
To objectPerson.prototype
Added a methodgetAge()
; - perform
4
, by the constructorPerson
An instance is createdulivz
It is worth noting that when a constructor is instantiated, it must automatically execute that constructor. - Get it in the browser
5
The output of, i.eulivz
It should be:
{
name: 'ulivz'.age: 24
__proto__: Object // This is actually 'person.prototype'
}
Copy the code
With the experience of the previous section, the following equation holds:
console.log(ulivz.__proto__ == Person.prototype) // true
Copy the code
- perform
6
When, as inulivz
Can’t find ingetName()
和getAge()
These two methods, then, continue to look up the prototype chain, which is passed__proto__
Look up, then, soon inulviz.__proto__
– that is,Person.prototype
, stops the search and executes the result.
This is JavaScript archetypal inheritance. To be precise, JavaScript prototype inheritance is implemented through __proto__ with the help of Prototype.
Thus, we can conclude as follows:
- Function object
__proto__
Point to theFunction.prototype
; (review) - Function object
prototype
Point to theinstance.__proto__
; (review) - Common object
__proto__
Point to theObject.prototype
; (review) - Normal objects do not have
prototype
Properties; (review) - An attempt is made to access a property/method of an object if it is not found on the current object
ob.__proto__
That is, the prototype of the constructor that accesses the objectobCtr.prototype
, if still not found, the search will continueobCtr.prototype.__proto__
, like search down in turn. If, at some point, the property is found, the value is returned immediately and the search for the prototype chain stops. If not, the property is returnedundefined
.
To test your understanding of the above, analyze the following two questions:
- What is the output of the following code?
console.log(ulivz.__proto__ === Function.prototype)
Copy the code
View the results
false
Person.__proto__
和Person.prototype.__proto__
Where do they point to?
View analysis
As mentioned earlier, in JavaScript everything is an object. Person is obviously an instance of Function, so person.__proto__ points to function. prototype:
console.log(Person.__proto__ === Function.prototype) // true
Copy the code
Because Person.prototype is a normal Object, person.prototype. __proto__ points to Object.prototype
console.log(Person.prototype.__proto__ === Object.prototype) // true
Copy the code
To verify that there is no Object in the prototype chain of person.__proto__ and that there is no Function in the prototype chain of person.prototype. __proto__, use the following statement:
console.log(Person.__proto__ === Object.prototype) // false
console.log(Person.prototype.__proto__ == Function.prototype) // false
Copy the code
Ultimate: Prototype chain diagram
In the last video, we actually left with a question:
- If the prototype chain goes on a search, if it can’t be found, when does it stop? In other words, where does the prototype chain end?
We can quickly verify this with the following code:
function Person() {}
const ulivz = new Person()
console.log(ulivz.name)
Copy the code
Obviously, the above output is undefined. The following is a brief description of the search process:
ulivz // is an object that can continue
ulivz['name'] // It does not exist
ulivz.__proto__ // is an object that can continue
ulivz.__proto__['name'] // It does not exist
ulivz.__proto__.__proto__ // is an object that can continue
ulivz.__proto__.__proto__['name'] // It does not exist
ulivz.__proto__.__proto__.__proto__ // null !!!! Stop the search and return undefined
Copy the code
Wow, the end of the road is empty.
Finally, go back to the demo code from the previous section:
const Person = function(name, age) {
this.name = name
this.age = age
} / * 1 * /
Person.prototype.getName = function() {
return this.name
} / * 2 * /
Person.prototype.getAge = function() {
return this.age
} / * * / 3
const ulivz = new Person('ulivz'.24); / * * / 4
console.log(ulivz) / * * / 5
console.log(ulivz.getName(), ulivz.getAge()) / * * / 6
Copy the code
Let’s draw a prototype chain, or let’s draw the whole prototype chain, right? See the picture below:
PS: Hanjian changed CHL (my Chinese initials) to Ulivz (Github’s name), so CHL in this image is actually Ulivz, which I drew when I was using Windows = =
Now that I’ve drawn this picture, almost all of my previous questions have been answered.
Instead of saying that everything is an object, it seems more graphic that everything is empty.
Seasoning: constructor
As mentioned earlier, only the prototype object has the constructor property, which is used to point to the function object that references it.
Person.prototype.constructor === Person //true
console.log(Person.prototype.constructor.prototype.constructor === Person) //true
Copy the code
This is a circular reference. Of course, you can also draw it in the prototype chain diagram in the previous section, so I don’t need to talk about it here.
Added: Archetypal inheritance of the six built-in (function) objects in JavaScript
Through the above discussion, combined with the corresponding code verification, sorted out the following prototype chain diagram:
Thus, we reinforce these two ideas:
- Of any built-in function object (class) itself
__proto__
All point toFunction
The prototype object of;- In addition to
Oject
Of the prototype object__proto__
Point to thenull
For all other built-in function objects__proto__
All point toobject
.
In order to reduce the time for readers to type the code, the verification code is given in the hope that it can promote your understanding.
Array:
console.log(arr.__proto__)
console.log(arr.__proto__ == Array.prototype) // true
console.log(Array.prototype.__proto__== Object.prototype) // true
console.log(Object.prototype.__proto__== null) // true
Copy the code
RegExp:
var reg = new RegExp;
console.log(reg.__proto__)
console.log(reg.__proto__ == RegExp.prototype) // true
console.log(RegExp.prototype.__proto__== Object.prototype) // true
Copy the code
Date:
var date = new Date;
console.log(date.__proto__)
console.log(date.__proto__ == Date.prototype) // true
console.log(Date.prototype.__proto__== Object.prototype) // true
Copy the code
Boolean:
var boo = new Boolean;
console.log(boo.__proto__)
console.log(boo.__proto__ == Boolean.prototype) // true
console.log(Boolean.prototype.__proto__== Object.prototype) // true
Copy the code
Number:
var num = new Number;
console.log(num.__proto__)
console.log(num.__proto__ == Number.prototype) // true
console.log(Number.prototype.__proto__== Object.prototype) // true
Copy the code
String:
var str = new String;
console.log(str.__proto__)
console.log(str.__proto__ == String.prototype) // true
console.log(String.prototype.__proto__== Object.prototype) // true
Copy the code
conclusion
A quick summary:
- if
A
throughnew
To create theB
,B.__proto__ = A.prototype
; __proto__
Is the starting point of the prototype chain search;- perform
B.a
If, in theB
Can’t find ina
, will be inB.__proto__
Student: Middle, which is equal toA.prototype
IfA.prototype
If you still don’t have it, you’ll keep looking up, and eventually, you’ll find itObject.prototype
If you can’t find it, becauseObject.prototype.__proto__
Point to thenull
, so it returnsundefined
; - Why is everything empty, or is there, as it were, at the top of the chain of archetypes
Object. The prototype. __proto__ - > null
.
Finally, I leave you with a question:
- How to use
JavaScript
What about the inheritance of the implementation class?
Take a look at the next article in my prototype series, In Depth JavaScript Inheritance.
Above, the end of the text.
Note: In addition, this article is a personal summary, and there may be some omissions in some expressions. If you find any deficiencies in this article, please feel free to point them out in the comments or give me an issue in order to avoid misinterpretation. Thank you ~