JavaScript prototype and prototype chain (detailed interpretation)

Prototype and prototype chain

1, Prototype overview

1. Concept

Prototype is a very important concept in JavaScript. It is the basis of object-oriented inheritance. You know, the design pattern of JavaScript is the prototype pattern (one of the 23 classic design patterns). It is precisely because it has this pattern that it is very flexible. More object-oriented design patterns can be implemented based on prototype patterns.

2, Prototype chain

1. What is a prototype chain?

Prototype chain is a prototype search mechanism and an addressing chain. In fact, the methods or attributes on the prototype are searched along the prototype chain in a certain order. If the prototype and method are still not found in the end, JS will throw an exception without this property or method.

2. What is a prototype pointer?

The prototype pointer is an address bridge connecting prototype objects. You will understand it by looking at the figure below

The prototype object consists of two parts, prototype data and prototype pointer. Prototype data is used to store attributes or methods, and prototype pointers are used to verify the prototype linked list for lookup

2. Prototype chain rules

A new instance Object. Its prototype pointer points to the prototype Object of this class. By default, the prototype Object pointer of this class points to the prototype Object of Object

A is a class. The prototype pointer of the new instance a points to a, and the prototype pointer of a points to the Object class by default.

In other words, the prototype chain is searched from beginning to end. If there is no such attribute on the a prototype, go to the a that created it. If there is no such attribute on the a prototype, go to the Object class. If there is still no prototype on the Object, an exception will be thrown

3, Implement class inheritance

There are two steps to implement class inheritance:

  • Inherit properties and methods from objects (object inheritance)
  • Inherit properties and methods in the object prototype (prototype chain inheritance)

Before we study them, we first establish two classes

function father() {
    //These properties are on the father instance object
    this.name = "cry"
    this.age = 50
}
//These methods are on the father prototype object
father.prototype.getName = function () {
    return "Father's name" + this.name
}
father.prototype.getAge = function () {
    return "Father, he is this year" + this.age + "Years old"
}

//Subclass (name, age)
function child() {

}

We first implement the inheritance of instance objects

function child() {
    //The first step of inheritance is the property inheritance of the instance object
    father.call(this)//call can pass the child context into Father for attribute assignment
}
//Now you can access father's name attribute and age attribute
var c1 = new child()
console.log(c1.name, c1.age)
//However, we still have no way to access the methods on the father prototype
console.error("error",c1.getAge())

The next step is prototype chain inheritance

We draw to understand prototype chain inheritance

Modify the previous prototype chain

Modified prototype chain

We insert a father prototype object node between object and child, so that if there are no subclass methods, we will find the methods under the father prototype down the prototype chain

Then the corresponding code is

//Modify the prototype pointer of child to point to the prototype object of father (previously, it points to object by default, as shown in the above figure)
child.prototype.__proto__ = father.prototype

Full inheritance code

//Parent class (name, age)
function father() {
    this.name = "cry"
    this.age = 50
}

father.prototype.getName = function () {
    return "Father's name" + this.name
}
father.prototype.getAge = function () {
    return "Father, he is this year" + this.age + "Years old"
}

//Subclass (name, age)
function child() {
    //The first step of inheritance is attribute inheritance
    father.call(this)//call can pass the child context into Father for attribute assignment
    //If you want to override override, override child ex:this. The attribute you want to override = xxx
}

//At this time, the child already has the name attribute and the age attribute
var c1 = new child()
console.log(c1.name, c1.age)
//However, the getName and getAge methods on the father prototype chain cannot be accessed yet
// console.error("undefined",c1.getAge()) will report an error

//Step 2: Inheritance -- prototype chain inheritance (_proto_ is the prototype pointer)

child.prototype.__proto__ = father.prototype
//If you want to override, override child ex below: child. Prototype. Method you want to override = function()
var c2 = new child()
console.log(c2.getName(), c2.getAge())
//Real inheritance can now be achieved

The above is the way to implement class inheritance

Run the screenshot and print the child instance

Red is the prototype object and purple is the prototype pointer

The above is how to use es5 to implement class inheritance. In fact, we can also implement inheritance in es6, which is very simple. We added the extend keyword, which is almost the same as java

Remember, when you print an object, the [[prototype]] you see is the prototype pointer. The properties and methods at the same level are the content / properties in the prototype + the content and properties in the instance object. Because attributes and contents have two storage containers, one is that instance objects can store them, and the other is that prototype objects can store them.

If you clearly view the prototype and prototype chain, print the instance objects, expand [[prototype]] one by one, and look at the constructor methods inside to know who they (prototype pointers) point to (as shown in the figure above)

4, Object.create()

We all know that Object.create() can create a new object instance for us. Unlike new, new can only pass objects with a constructor. Our Object.create() can create objects without threshold.

For example (demonstrate the difference)

let obj={
    a:1,
    func:function(){
        console.log("I wish Turing studio better and better!")
    }
}
Object.create(obj)//No error will be reported
new obj()//An error will be reported because obj does not have a constructor and does not belong to an object of constructor type

The above explains the difference between Object.create() and new

Now we need to re implement the Object.create() method with the prototype chain. Yes, it is the handwritten source code!

function create (obj){
    function fn(){}
    //Replace the prototype object of fn with obj (the prototype object includes prototype pointer [addressing part] and prototype attribute / method [data part])
    fn.prototype=obj
    //In this way, the prototype pointer of the instance object points to the fn class, and the fn prototype is obj, so new fn inherits the obj prototype and can use everything in the prototype
    return new fn()
}

We use diagrams to understand this process

The corresponding code is fn.prototype=obj [modify fn prototype, fn now the prototype is obj]

Remember that for an instantiated object, its prototype pointer points to its prototype object, that is, new fn points to FN. Because the prototype of FN is already obj, what you get out of new can be regarded as obj. It is no different from new obj(). The only difference is that direct new obj() may report an error, because as I said above, things without a constructor cannot be new. However, I modified the prototype of function fn to make its prototype obj. It must be new, because function has a constructor.

Complete case

function create (obj){
    function fn(){}
    //Replace the prototype object of fn with obj (the prototype object includes prototype pointer [addressing part] and prototype attribute / method [data part])
    fn.prototype=obj
    //In this way, the prototype pointer of the instance object points to the fn class, and the fn prototype is obj, so new fn inherits the obj prototype and can use everything in the prototype
    return new fn()
}
let b={
    a:'Hello',
    sayhello:function(){
        return "hello"
    }
}
let cc=create(b)
console.log(cc.sayhello())

5, Implement the new keyword

Do you know how the bottom layer of the new keyword is implemented? Yes, prototype and prototype chain. Let's make the prototype pointer of an object point to the prototype of the specified object

function new_(classify){
    let obj={}
    classify.call(this)
    obj.__proto__=classify.prototype
    return obj
}

Using it, you can not only access the properties and methods of the constructor (also known as the properties / methods in the object instance), but also access the properties / methods on the prototype chain

I'll share so much this time. I'll see you next time!

Tags: Javascript html5

Posted on Wed, 15 Sep 2021 20:46:50 -0400 by deadoralive