Understand prototype / prototype chain

For the front-end partners, whether novices or old birds, I think they have been tossed about the prototype. It's always a vague feeling. If the prototype doesn't understand, how can you say you're the front-end siege lion?

About objects

When it comes to object-oriented (OO), your first reaction must think of concepts such as class, object and interface implementation. Why do we talk about object here? Because there is no class in ECMAScript, and because the function in ECMAScript has no signature, there is no interface.

The object in ECMAScript-262 is defined as "a collection of unordered attributes, whose attributes can be basic values, objects or functions". Therefore, from the perspective of data structure, objects can be regarded as hash tables.

Object classification

From the way of creating objects, objects can be divided into three categories: built-in objects, host objects and custom objects. More about object classification Click here.

It should be emphasized that except for the six basic types of number, string, boolean, null, undefined and symbol, all other types are objects (reference types), including functions. All functions are objects, and vice versa.

Relationship between objects and functions

Object creation

As mentioned earlier, there are no classes in ECMAScript, so how to create objects?

Object Literal
// Method 1: object literal
var zhangsan = {
    type: "human beings",
    name: "Zhang San",
    age: 18,
    greeting: function() {
        console.log(`hello I'am ${this.name}`);
    }
};
zhangsan.greeting(); // "hello I'am Zhang San"

This method mainly has the following problems:

  • When you want to create multiple variables, you have to write a lot of duplicate code;
  • Each instance will hold a greeting function, but in fact, the functions are the same. There is no reuse and waste of resources;
  • Create all instances of "human" (type = "human"). The value of type is the same, but each instance still holds an independent copy;
  • The type of the created instance cannot be recognized (that is, the specific type of the created instance is not known, only that it is an instance of Object).
Factory mode
// Mode 2: factory mode
function createPerson (name, age) {
    var p = new Object();
    p.type = "human beings";
    p.name = name;
    p.age = age;
    p.greeting = greeting;
    return p;
}
var lisi = createPerson ("Li Si", 20);
lisi.greeting(); // "hello I'am Li Si"
function greeting () {
    console.log(`hello I'am ${this.name}`);
}

Although the second method is encapsulated to avoid a large number of duplicate codes during creation, and solves the problem that multiple instances hold multiple copies of the meeting by pulling the meeting out of the global scope, it also introduces a function that can only be used by instances of this type into the global space, polluting the global space; Finally, it can also solve the problem of object recognition.

// Method 3: constructor
function Person (name, age) {
    this.type = "human beings";
    this.name = name;
    this.age = age;
    this.greeting = greeting;
}
var wangwu = new Person("Wang Wu", 24); // wangwu instanceof Person === true
wangwu.greeting(); // "hello I'am king five"
function greeting () {
    console.log(`hello I'am ${this.name}`);
}

This method is almost perfect and solves the problem of object recognition, but it still does not solve the problem of shared function polluting the global space; In order to solve this problem, let's show our protagonist prototype.

Prototype & prototype chain

Finally, let's get to the point. To solve the problems faced in the above method 3, we need to have an object that belongs to the constructor exclusive (not defined to the global pollution global space) and can be shared by all object instances created by the constructor. This object is the prototype (or prototype object).

What is a prototype

By default, any function has an attribute prototype, which is a pointer to an object (prototype object). The purpose of the prototype object is to contain the attributes and methods shared by specific type instances. By default, the prototype object has only one constructor attribute. We can define more attributes and methods for it.

// Mode 4: prototype method
function Person (name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.type = "human beings";
Person.prototype.greeting = function () {
    console.log(`hello I'am ${this.name}`);
};
var wangwu = new Person("Wang Wu", 24); // wangwu instanceof Person === true
wangwu.greeting(); // "hello I'am king five"

How does the above example wangwu find the greeting defined in the Prototype object? The reason is that all objects have an internal pointer to the Prototype object of the instance constructor, which is called [[Prototype]] in ECMAScript-262 version 5. Although the standard does not define how to access this internal pointer, Firefox, Safari and Chrome support an object named__ proto__ Pointer properties.

View the properties of wangwu in the chrome console, as shown in the following figure:
[uploading pictures outside the station... (image-1d07-164413611733)]

Prototype chain lookup

When an object instance accesses a property or calls a method, first find it in its own property. If it finds it, it will return the value or initiate the call. If it doesn't, it will follow__ proto__ Look up until you finally find the object If the prototype is still not found, it will terminate and report an error.

The relationship among object instance, constructor and prototype object of constructor is as follows:

The red path and in the figure above are the search direction. This one has__ proto__ The chain of pointers is the prototype chain. The essence of prototype chain is a list of pointers pointing to prototype objects in sequence.

Dynamics of prototype

Because the object instance__ proto__ It is just a pointer to the prototype object, so the modification of the prototype object can be reflected on the instance immediately, even if the instance is created before modifying the prototype:

Person.prototype.work = function () {
    console.log('work function');
}
// wangwu here is the instance created above. After adding the work method to the prototype, it can be called immediately
wangwu.work(); // "work function"

However, if the entire prototype object is rewritten, it is equivalent to specifying a new prototype object for the constructor, and the instance that has been created is invalid__ proto__ It still points to the old prototype object, so the method defined in the new prototype cannot be accessed:

Person.prototype = {
    work: function () {
        console.log('work function');   
    }
};
// report errors
wangwu.work(); // "wangwu.work is not a function"
// The instance created after modifying the prototype object because the obtained__ proto__ Property points to the new prototype, so no error will be reported
var sanma = new Person('Sanmao', 30);
// Can be happy "work"
sanma.work(); // "work function"

[image upload failed... (image-b3adea-164413611733)]

After covering the whole prototype object, the original prototype point in the above figure is cut off and points to the new prototype.

To summarize

By default (because the prototype object is actually writable, it can be changed):

  1. Any function has a pointer attribute prototype that points to its prototype object;
  2. Any object instance has an internal pointer [[prototype]] (_proto_);
  3. Prototype objects are also objects, so there are__ proto__ (for example, point to Object.prototype in the figure above);
  4. Object instance__ proto__ Pointer to the prototype object of the constructor: Wangwu__ proto__ === Person. prototype ;
  5. The constructor property of the prototype object points to the constructor: person prototype. constructor === Person;
  6. The constructor is not directly related to the object instance, but has a pointer attribute pointing to the same prototype object.

Object instance identification (detection)

As we know, for number, string, boolean, undefined and function values, we can simply distinguish them by typeof operator. However, for reference type instances other than function and null, typeof returns "object", but if we distinguish them in detail, if an object instance is an instance of God type, typeof can't help it.

instanceof operator

To identify the specific object instance type, you need to use the instanceof operator. The format is instance instanceof Func. Instance is the instance object to be detected, and Func is a constructor. With the understanding of the prototype chain above, the detection mechanism of instanceof is much simpler, only one on the prototype chain of instance__ proto__ If it points to the prototype object of Func, it returns true; otherwise, it returns false. Namely:

instance.__proto__...__proto__ === Func.prototype

You can also use func prototype. isPrototypeof(instance),Object.getPrototypeof(instance) === Func.prototype to judge.

console.log(wangwu instanceof Person); // true
console.log(wangwu instanceof Object); // true
console.log(Person.prototype.isPrototypeof(wangwu)); // true
console.log(Object.prototype.isPrototypeof(wangwu)); // true
console.log(Object.getPrototypeof(wangwu) === Person.prototype); // true
console.log(Object.getPrototypeof(wangwu) === Object.prototype); // false, because the getPrototypeof function only returns the instance prototype and does not return other prototypes in the prototype chain

Prototype inheritance

After understanding the prototype, the prototype inheritance is very simple. The class to be extended can point to the prototype of the parent class. The following is a simple prototype inheritance implementation:

function Men() {
 // 
}


Men.prototype = Object.create(Person.prototype);
Men.prototype.constructor = Men;

In particular, after assigning a value to the prototype attribute, Men prototype. The constructor points to Person, so you must point it back to Men.

Keywords: Javascript prototype

Added by Fitzlegend on Sat, 12 Feb 2022 05:28:30 +0200