This article covers key words: constructor, instance object, __proto__ attribute, Prototype object, constructor attribute, prototype chain, prototype chain inheritance

In line with the teaching package will, will not see a few times, forget to see a few times can also want to principle, JS prototype part involves the concept of a clear. Future ES6 Class syntactic candy will be updated based on this article!

The following paragraph comes from MDN, which feels very comprehensive. I hope I can thoroughly understand the meaning of the following paragraph after reading this article!

JavaScript is often described as a prototype-based language. Each object has a prototype object from which it inherits methods and properties. A stereotype object may also have a stereotype from which it inherits methods and properties, layer by layer, and so on. This relationship, often referred to as the Prototype chain, explains why one object has properties and methods defined in other objects.

Through prototypes, objects in JavaScript inherit features from other objects; This inheritance mechanism is different from that of classical object-oriented programming languages.

To be precise, these properties and methods are defined on prototype properties above the Object’s constructor functions, not on the Object instance itself.

In traditional OOP, a “class” is first defined, and then all properties and methods defined in the class are copied into the instance when an object instance is created. This is not replicated in JavaScript — instead, a link is made between the object instance and its constructor (which is the __proto__ property, derived from the constructor’s prototype property), and then those properties and methods are found in the constructor by going up the prototype chain.

The outline of this paper is divided into three parts:

  • The first part is to understand the JS prototype chain
  • Part 2 What exactly does the new operator do? Implement a new!
  • Part 3 how does JS implement inheritance? Prototype chain inheritance!

Let’s get to the subject ~

The first part is to understand the JS prototype chain

1. Constructor, instance object

  • Person is an instance object of the Person function
  • The Person function is the constructor of Person
function Person() {}
let person = new Person();
person; // Person {__proto__} is an object with a __proto__ attribute
person.constructor == Person; // true points to the Person constructor itself
Copy the code

The constructor property is simply a property that holds a reference to its own constructor. The constructor property of the instance object points to the constructor itself

(Actually, you can’t say that. As we’ll see later, the Person instance object points to prototype via its __proto__ attribute, whose constructor attribute points to person itself. The result is that the person itself has no constructor, but the person. Constructor can be printed.)

2. Built-in JS objects Object and Function

  1. Function and Object functions are built-in JS objects, also called inner classes. These built-in functions can be used as constructors.

  2. Why “functions are objects”? About the particularity of the Function object.

function Person() {}
Person.constructor == Function; // The true constructor attribute points to the built-in Function Function
Copy the code
  • All functions are instance objects of functions, so it is often said that functions are objects, and everything in JS is an object.
// Both point to built-in Function
Function.constructor == Function; // true
Object.constructor == Function; // true
Copy the code
  • Function is also its own constructor
  • Function is also a constructor for built-in objects such as Object.
let obj = {};
obj.constructor == Object; // true The normal Object's constructor attribute points to the Object constructor
Copy the code
  • Note Common objects are also instances of the built-in Object function.

Rearrange the constructor pointing chain:

  • Person. Constructor — > Function Function, Function. Constructor — > Function itself

  • Obj. Constructor — > Object, Object. Constructor — > Function Function, Function

3. Prototype object

  1. To enable instance objects to share some properties and methods, they are all placedConstructor's prototype object, prototypeOn.
function Person() {}
Person.prototype.sayHello = function () {
  console.log("Hello!");
};
let person1 = new Person();
let person2 = new Person();

person1.sayHello === person2.sayHello; // true
Copy the code

All functions are instance objects of Function functions, so Function functions also have a prototype object that holds the shared properties and methods of its own instance object.

  1. Constructor is also a shared property, stored in the prototype object, that still points to its own constructor.
function Person() {... } Person.prototype.constructor == Person// true
Copy the code

See the relationship between instance objects, constructors, prototype objects, and constructor.

The constructor property of an instance object points to that instance object’s constructor, which exists as a shared property on the constructor’s Prototype.

🤔 : Person. constructor points to the Person constructor. How do person and constructor relate to each other? How do you make instance objects find their own prototype objects? Left left left

4. __proto__attribute

  1. Let’s start with the conclusion, which is the core formula for understanding prototype chains:person.__proto__ == Person.prototype. Of the instance object__proto__Property points to the constructor’s prototype objectprototype.

The shared property constructor can be found by creating an attribute __proto__ inside the Person object that points directly to its prototype object. Constructor now points to the instance object’s constructor, Person().

To reinterpret some of the first examples in the opening paragraph, it should look something like this

function Person() {}
let person = new Person();
// Recite it, please!!
person.__proto__ == Person.prototype;
// constructor exists on prototype and points to the constructor
Person.prototype.constructor == Person;
// So person.__proto__ can read the constructor on prototype, pointing to the constructor
person.__proto__.constructor == Person;

// Why is the opening equation true?
person.constructor == Person; // True is also true?
// This is because of the prototype chain lookup, more on that later. Because Person finds Constructor through the prototype chain and points to the Person constructor.
/ / but pay attention to
Person.constructor == Function; // Because the Person constructor is Function
Copy the code

On the particularity of Function:

  1. Since all functions in JS are instance objects of the Function Function, the Person Function also has one__proto__Property refers to its own prototype object, the prototype of the Function Function.
  2. Function also has a__proto__The property points to itself because it takes itself as its constructor
  1. about__proto__The second important concept is that since the prototype object Prototype is also an object, it also has a__proto__Property that points to its own prototype object. So what’s its constructor?

Person. Prototype.__proto__ == Object. Prototype

The prototype Object inside the function is an instance Object of the built-in function Object.

Summary: The __proto__ attribute of the instance object points to the constructor’s prototype object; All prototype Object __proto__ attributes refer to Object prototype objects!

It’s a little convoluted, but that’s the core of how we understand the prototype chain!!

The following diagram describes the relationship between __proto__ and prototype objects:

5. Here comes the prototype chain

So with that in mind, what is a prototype chain?

  • Each instance object has a private attribute __proto__ pointing to its constructor’s prototype object. The prototype object also has its own private attribute __proto__ pointing to the prototype object… Layer up until the prototype Object of an Object (the built-in Object) is null and is the last link in the prototype chain.

  • By associating each instance object with the prototype object, the associated prototype object is also someone else’s instance object, thus forming the form of a chain, which is called the prototype chain.

  • The Object function is the constructor that traces all objects back to the root through the prototype chain. The “last Prototype Object” is the prototype Object inside the Object function.

  • The prototype Object’s __proto__ points to null. Why can’t an Object Function have its __proto__ attribute refer to its prototype as Function does? The answer is that if you point to your prototype, you’re going to go into an infinite loop when you can’t find a property along the prototype chain, so you have to point to null, which is an escape condition.

In the same image, you can find four prototype chains using the Person constructor:

Article 1: person.__proto__ –> Person.prototype –> Person.prototype.__proto__ –> Object.prototype –> Object.prototype.__proto__ –> null

Article 2: Person.__proto__ –> Function.prototype –> Function.prototype.__proto__ –> Object.prototype –> Object.prototype.__proto__ –> null

Article 3: Function.__proto__ –> Function.prototype –> Function.prototype.__proto__ –> Object.prototype –> Object.prototype.__proto__ –> null

Article 4: Object.__proto__ –> Function.prototype –> Function.prototype.__proto__ –> Object.prototype –> Object.prototype.__proto__ –> null

So let’s take a quick look at what’s on instance object P

function P(){};
let p = new P(); p;
/ / left left left
> P {}
  > __proto__: Object // Instance p has a __proto__ attribute on it that points to the constructor's prototype object
    > constructor: Æ’ P() / /prototypeThere are public methods onconstructor
    > __proto__: / /prototypeThere are still__proto__Property pointing to built-inObjectPrototype object >constructor: Æ’ Object() / / insideObjectOn the prototype objectconstructorAs well ashasOwnPropertyPublic method, etc.hasOwnProperty: Æ’ hasOwnProperty()...Copy the code

So what’s the difference between constructor properties and methods on this and on Prototype?

The properties and methods defined by this are unique to the generated instance. The properties and methods defined on Prototype are common to each instance. Changes to properties and methods defined in Prototype are picked up by each instance object.

Here’s an example:

function Person() {
  this.age = 0;
  this.list = [];
}
let p1 = new Person();
p1.list.push(1);
p1.list; / / [1]

let p2 = new Person();
p2.list.push(2);
p2.list; / / [2]
Copy the code

The properties and methods defined on Prototype are common to each instance:

function Person() {
  this.age = 0;
}
Person.prototype.list = [];

let p1 = new Person();
p1.list.push(1);
p1.list; / / [1]

let p2 = new Person();
p2.list.push(2);
p2.list; / / [1, 2]
Copy the code

Front 1~5 section summary!! Highlight!!

  1. What’s on a function/object?
  • Object has__proto__attribute
  • Function has__proto__Properties, and prototype objectsprototype
  • A prototype objectprototypeThere are also__proto__Attributes, andconstructorProperties, and some shared properties and methods (hanging on Prototype)
  1. __proto__ is the browser implementation of the view prototype scheme, also written as [[prototype]]. The __proto__ property is used to access an object property that does not exist in the object, and then the __proto__ property is used to locate the object (parent object) until the __proto__ property terminates at null. If you go any higher, you’re going to evaluate it on null, and you’re going to get an error. The link of objects connected by the __proto__ attribute is called the prototype chain.

  2. The constructor property is simply a property that holds a reference to its own constructor. The constructor property of the instance object points to the constructor itself.

  3. The prototype object lets objects instantiated by this function find common properties and methods

Part 2 What exactly does the new operator do?

New operator principle analysis

Combined with the analysis of the above principle, let’s look at the new keyword to achieve what functions?

function Person(age) {
  this.age = age;
}
let p = new Person(20);
p; // Person {age:20}
Copy the code
  1. First we create an empty object p = {}
  2. And then I’m going to take p__proto__Property points to a prototype object whose constructor, Personprototype
  3. Will constructor internalthisBind to the new object P and execute the constructorPerson()(In fact, and transfer ordinary function, and the valuethis.age = 20)
  4. If the constructor returns a non-reference type, the new object p is returned. Otherwise return the value of the reference type.

Write a new implementation by hand

Can’t write the principle, not really understand the principle, we begin to manually implement ~

function Person(name, age) {
  this.name = name;
  this.age = age;
  // The constructor itself may return a result
  // return {
  // name,
  // age,
  // test:123
  // }
}

function _new(Func, ... rest) {
  1. Define an instance object
  let obj = {};
  // 2. Manually point the __proto__ attribute in the instance object to the corresponding prototype object
  // Obj. constructor points to the Person function, which obJ has recognized as its own constructor
  obj.__proto__ = Person.prototype;
  // 3. obj needs to be able to call constructor private properties/methods
  // The constructor needs to be called within the execution environment of the instance object, adding the private properties/methods set by the constructor
  let res = Person.apply(obj, rest);
  // 4. If an object is returned from a constructor, the result is returned directly (as if the function was called directly); Otherwise return a new object.
  return res instanceof Object ? res : obj;
}

let p = _new(Person, "Zhang"."20");
p; // Person{age: "20", name: "张三"}
p.constructor; / / Æ’ Person () {}
Copy the code

In step 3 above, the constructor is a special method used to initialize an object when it is created, that is, to assign initial values to its member variables. Statements in the body of a function are not executed immediately after a function is declared, but only when it is actually called. If not called, this has no reference and age has no value. The apply method completes the function call, assigns initial values to its object member variables, and binds the reference to this to the instance object P.

The third part is prototype chain inheritance

ES6 classes can be inherited through the extends keyword, which is actually much cleaner and more convenient than ES5’s inheritance through modified prototype chains. But the text focuses on how JS implemented inheritance before the advent of Class. Further extensions to Class inheritance in ES6 will be provided in a future series of articles.

ES5 inheritance essentially creates an instance object of the subclass, this, and then adds the Parent class’s methods to this (parent.apply (this)).

1. Implement inheritance using the Call method

function Father() {
  this.id = "1999";
}
function Son(age) {
  this.age = age;
  // Inheritance is implemented by calling the call method of the parent function
  Father.call(this);
}
let p = new Son(20);
p; // Son {age: 20, id: "1999"} has the parent's private attributes
p.id; / / 1999
Copy the code

Instantiate an object p, implement two steps: Son this refers to p, Father this refers to Son. So p has age, id.

2. Prototype chain inheritance

function Father(id) {
  this.id = id;
}
function Son(age) {
  this.age = age;
}
// Son changes its prototype pointer
// Son.prototype.__proto__ refers to the Father instance, and the Father instance refers to the Father prototype object.
// Son. Prototype.__proto__ == Father. Prototype
Son.prototype = new Father("1999");
let p = new Son(20);

p; // Son {age: 20} p does not have the parent's private attribute id, but it can be accessed
p.id; / / 1999
p.__proto__; // Father {id: "1999"}

p.hasOwnProperty("age"); // true
p.hasOwnProperty("id"); // false
Copy the code

I don’t know if you have such doubts, why not write p.__proto__. Id instead of p.ID can get it? This is the search process for the prototype chain:

  1. Prototype = Father; / / Son. Prototype = Father; / / Son. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

  2. In the same way, If the prototype object does not have a property, the __proto__ constructor that creates it does not have a property on the prototype object. The Prototype Object itself also has a __proto__ attribute pointing to its own prototype Object (currently the built-in Object), which has shared properties and methods left behind by the constructor. Such as hasOwnProperty(), Valueof (), and so on.

  3. The __proto__ property is used to access an object property that does not have the property, and then the __proto__ property refers to the object (parent object) until the __proto__ property terminates at null. If you go any higher, you’re going to evaluate it on null, and you’re going to get an error. The link of objects connected by the __proto__ attribute is called the prototype chain.

That’s all there is to it!

conclusion

This paper analyzes the basic principle of prototype chain, how to implement the new keyword, and inheritance implementation in ES5 from three parts.

Which begs the question, what changes have been made to the ES6? Class extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends

reference

Inheritance and prototype chains

The object prototype

Understand constructor, Prototype, Proto, and prototype chains in your own way.

Help you understand JS prototype completely.protoWith constructor (diagram)