Deep understanding of javascript prototypes and prototype chains

Posted by ljschrenk on Tue, 30 Nov 2021 07:40:43 +0100

Constructor

The function that instantiates an object through the new function name is called a constructor.
Any function can exist as a constructor. The reason why there are constructors and ordinary functions is mainly different in function. The main function of constructors is to initialize objects, which is characterized by using them together with new. New is creating an object from scratch. The constructor is adding properties and methods to the initialized object. Constructors are defined with an uppercase initial (canonical).

function Person(name) {
    this.name = name;
}

let p1 = new Person('Zhang San'); // instantiation 

console.log(p1); // Person {name: "Zhang San"}

At this point, p1 is a new object.

1. What happened in the process of creating a new object?

  1. Create an empty object obj {}
  2. Empty object_ proto_ Points to the prototype member object of the constructor
  3. Using apply to call the constructor function, properties and methods are added to the object referenced by this
  4. If there is no other object returned in the constructor, return this, that is, the new object of the created. Otherwise, return the object returned in the constructor

Understanding of new: new applies for memory and creates objects. When new is called, the background will implicitly execute new Object() to create objects. Therefore, the strings and numbers created through new are reference types, but non value types.

2. Handwritten new function

function _new(func, ...args) {
    // 1. Create an empty object
    let obj = {};
    // 2. Of empty objects_ proto_ Points to the prototype member object of the constructor
    obj.__proto__ = func.prototype; // One or two steps of merging is equivalent to let obj = Object.create(func.prototype)
    // 3. Use apply to call the constructor function, and the properties and methods are added to the object referenced by this
    let result = func.apply(obj, args);
	// 4. Make sure that new comes out with an object
    return typeof result === 'object' ? result : obj;
}

Test case:

function Person(name, age) {
  this.name = name;
  this.age = age;
}

let obj = _new(Person, 'xia', 20);

console.log(obj); // Person {name: "xia", age: 20}

3. Method on constructor

  1. Define methods directly on constructors (not shared)
function Person() {
    this.say = function () { // Direct definition method
        console.log('hello');
    }
}

let p1 = new Person();
let p2 = new Person();
p1.say(); // hello
p2.say(); // hello

console.log(p1.say === p2.say); // false

Obviously, p1 and p2 don't point to the same place. Therefore, this method is used to add methods to the constructor to generate instances. Each time an instance is generated, a new memory space is opened to store methods. This can lead to a great waste of memory and affect performance.

  1. Add method through prototype (shared)

Constructors are functions allocated through prototypes and are shared by all objects.

function Person(name) {
    this.name = name;
}
Person.prototype.say = function () { // Adding methods through prototypes
    console.log('hello ' + this.name);
}

let p1 = new Person('Zhang San');
let p2 = new Person('Li Si');
p1.say(); // hello Zhang San
p2.say(); // hello Li Si

console.log(p1.say === p2.say); // true

Therefore, we often define public attributes in constructors and put public methods on prototype objects.

Click to view“ Five inheritance methods of constructor"

prototype

1. What is a prototype?

The above Person.prototype is the prototype (also known as explicit prototype). It is an object, which we also call prototype object.

2. What is the role of prototype?

The role of prototypes is to share methods.
Through Person.prototype.say, we can share methods without repeatedly opening up storage space and reducing memory waste.

3. What is the direction of this in the prototype?

Point to instantiated objects p1 and p2

Function object

  1. __Proto: any object (everything in JS is an object) has the proto attribute (implicit prototype)
  2. Prototype: all functions (functions only) have the prototype attribute (explicit prototype)
  3. Constructor: all prototype and instantiated objects have a constructor attribute, which points to the associated constructor itself

When we declare a method with function keyword, we will add a prototype property for this method, which points to the default prototype object, and the constructor property of this prototype also points to the method object. These two properties will be referenced by the property of the object when creating the object.

function Hello() {}; // Constructor
var h = new Hello();  // Instantiate object


// All functions have a prototype attribute (explicit prototype)
console.log(Hello.prototype); // Object {} prototype object
// The prototype property of the constructor has a constructor property that points to the constructor itself
console.log(Hello.prototype.constructor === Hello); // true
// The instantiated object does not have the prototype attribute, and only the function has the prototype attribute
console.log(h.prototype); // undefined


// The constructor property of the instantiated object points to the constructor itself
console.log(h.constructor === Hello); // true
// Namely
console.log(h.constructor === Hello.prototype.constructor); // true 


// All reference types have the _proto attribute (implicit prototype)
console.log(h.__proto__ === Hello.prototype); // true  
// Namely
console.log(h.__proto__ === h.constructor.prototype); //true
// Namely
console.log(Hello.prototype === h.constructor.prototype); //true
// Namely
console.log(Hello === h.constructor); // true


1. prototype

All functions (functions only) have the prototype attribute (explicit prototype)

function Person() {};

Person.prototype.sayHello = function() {
    console.log('Hello!')
}

var person1 = new Person();
var person2 = new Person();

console.log(person1.sayHello === person2.sayHello) // true, the same method

The prototype object is used to put the shared properties and methods of an instance of the same type. In essence, it is for the sake of memory.

At this point, you need to know that all functions themselves are instance objects of Function functions, so there will also be a prototype object in the Function function Function to put its own shared properties and methods of instance objects.

// The constructor property of the instantiated object points to the constructor itself
console.log(person1.constructor === Person); // true
console.log(person2.constructor === Person); // true

// Person is an instance object of a Function
console.log(Person.constructor === Function); // true
console.log(Function.constructor === Function); // true

As shown below:

2. _proto _

Any object (everything in JS is an object) has the _proto _attribute (implicit prototype)

function Person() {};

Person.prototype.sayHello = function() {
    console.log('Hello!')
}

var person1 = new Person();
var person2 = new Person();

// All reference types have a _proto _ attribute that points to the display prototype of the constructor
console.log(person1.__proto__ === Person.prototype); // true  
console.log(person2.__proto__ === Person.prototype); // true  

/*1,Literal mode*/
var a1 = {};
console.log(a1.constructor === Object); // true (constructor Object)
console.log(a1.__proto__ === a1.constructor.prototype); // true
console.log(a1.__proto__ === Object.prototype); // true


/*2,Constructor mode*/
var A = function (){}; 
var a2 = new A();
console.log(a2.constructor === A); // true (constructor function A)
console.log(a2.__proto__ === a2.constructor.prototype); // true
console.log(a2.__proto__ === A.prototype); // true


/*3,Object.create()mode*/
var a1 = {a:1} 
var a2 = Object.create(a1);
console.log(a2.constructor === Object); // true (constructor Object)
console.log(a2.__proto__ === a1); // true 
console.log(a2.__proto__ === a2.constructor.prototype); //false

3. constructor

All prototype and instantiated objects have a constructor attribute, which points to the associated constructor itself

function Person() {};
var person1 = new Person();
var person2 = new Person();

console.log(person1.constructor === Person); // true
console.log(Person.constructor === Function); // true
console.log(Function.constructor === Function); // true

  1. person1 and person2 are instances of the person object, and their constructor points to the constructor that created them, that is, the person function;
  2. Person is a Function, but it is also a Function instance object. Its constructor points to the constructor that created it, that is, the Function function;
  3. Function function, which is the built-in object of JS, and its constructor is itself, so the internal constructor attribute points to itself.

Therefore, the constructor property is actually a property used to save its constructor reference. There is no other special place.

Prototype chain

var A = function () {};
var a = new A();

// Prototype chain composed of _proto
console.log(a.__proto__ === A.prototype); // true
console.log(A.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // True null means "no object", that is, there should be no value there.

The chain structure composed of interrelated prototypes in the figure below is the prototype chain, that is, the blue line

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.say = function () {
    console.log('hello', this.name);
};

let student = new Person('Zhang San', 18);

console.log(student.__proto__ === Person.prototype); // true
console.log(student.__proto__.say === Person.prototype.say); // true
console.log(student.__proto__.say === student.say); // true
console.log(student.say === Person.prototype.say); // true

The reason why an object can use the constructor prototype object's properties and methods is that the object has a _proto _prototype

function Parent(month){
    this.month = month;
}

var child = new Parent('Ann');

console.log(child.month); // Ann
console.log(child.father); // undefined

When finding an attribute in child, the following steps are performed:

The access link is:

Ultimate graph

If you understand this figure, the prototype and prototype chain will be basically understood.

Topics: Javascript