Prototype and prototype chain

preface

Prototype and inheritance are two very important concepts in JS. A thorough understanding of the prototype is also the premise of learning inheritance well.

Let’s look at the relationship between constructors, instances, and prototype objects

“There is a direct connection between the instance and the prototype object, but not between the instance and the constructor.”

Two concepts

Js is divided into “function objects” and “normal objects”. Each object has a __proto__ attribute, but only function objects and “non-arrow functions” have a Prototype attribute.

  1. attribute__proto__Is an object [instance through__proto__Implicit archetype refers to its prototype object, which has two properties,constructorand__proto__;
  2. The prototype object has a defaultconstructorProperty to record which constructor the instance was created by;

The prototype

To understand the prototype

When you create a function (a non-arrow function), you create a Prototype property (pointing to the prototype object) for that function according to specific rules. By default, all stereotype objects automatically get a property called constructor that refers back to the constructor associated with them. When you customize a constructor, the stereotype Object acquires only the constructor attribute by default; all other methods inherit from Object. Each time the constructor is called to create a new instance, the inner [[Prototype]] pointer of that instance is assigned to the constructor’s Prototype object. There is no standard way to access the [[Prototype]] feature in scripts, but Firefox, Safari, and Chrome expose the __proto__ property on each object, which allows access to the Prototype of the object.

Person.prototype.name = "Nicholas"; person.prototype. name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function() { console.log(this.name); }; Let person1 = new Person() let person2 = new Person() Constructors have a prototype Object associated with the console. The log (Object. The prototype. ToString. Call (Person. The prototype)) / / [Object Object] console.log(Person.prototype) // {constructor: ƒ} // The constructor has a prototype attribute referencing its prototype object, which in turn has a constructor attribute referencing the constructor. In other words, Circular reference between the console. The log (Person) prototype) constructor = = = Person); // true // The constructor, prototype object, and instance are three completely different objects console.log(person1! == Person); // true console.log(person1 ! == Person.prototype); // true console.log(Person.prototype ! == Person); // true // Instance is linked to the Prototype object by __proto__, which actually points to the hidden property [[Prototype]] // Constructor is linked to the Prototype object by the Prototype property. Instances are not directly linked to the constructor, but directly to the Prototype object. Console. log(person1.__proto__ === person.prototype); // true conosle.log(person1.__proto__.constructor === Person); // true // Two instances of the same constructor that share the same prototype object console.log(person1.__proto__ === person2.__proto__); // true // object.getProtoTypeof (), returns the value of the internal property of the parameter [[Prototype]], used to get the Prototype Object, Console. log(object.getPrototypeof (person1) == person.prototype); // trueCopy the code

The diagram below:

Person. The prototype to the prototype object, and the Person. The prototype. Its constructor refers back to the Person the constructor. The prototype object contains the Constructor attribute and other attributes added later. Person1 and person2, two instances of Person, each have only one internal attribute that refers back to Person.prototype, and neither has a direct connection to the constructor.

Prototype level

When a property is accessed through an object, a search is initiated by the name of the property. The search begins with the object instance itself. If a given name is found on this instance, the value of that name is returned. If the property is not found, the search follows the pointer into the prototype object, and when the property is found on the prototype object, the corresponding value is returned. Therefore, when person1.sayName() is called, a two-step search occurs. First, the JavaScript engine asks, “Does the Person1 instance have the sayName attribute?” The answer is no. Then, go ahead and ask, “Does the prototype for Person1 have the sayName attribute?” The answer is yes. The function saved on the prototype is returned. When person2.sayname () is called, the same search process occurs and the same results are returned. This is how stereotypes are used to share properties and methods across multiple object instances.

Prototype chain

Review the relationship between constructors, stereotypes, and instances: each constructor has a Prototype pointing to the prototype object, the prototype object has a constructor property pointing back to the constructor, and the instance has an internal pointer to the prototype. What if the stereotype is an instance of another type? That means that the stereotype itself has an internal pointer to another stereotype, which in turn has a pointer to another constructor. This creates a chain of stereotypes between the instance and the stereotype. This is the basic idea of a prototype chain.

function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function () { return this.property; }; function SubType() { this.subproperty = false; } // SuperType subtype. prototype = new SuperType(); SubType.prototype.getSubValue = function () { return this.subproperty; }; let instance = new SubType(); console.log(instance.getSuperValue()); // trueCopy the code

The types SuperType and SubType define an attribute and a method, respectively. The main difference between the two types is that SubType inherits from SuperType by creating an instance of SuperType and assigning it to its own prototype subtType. prototype. This assignment overrides the original prototype of SubType, replacing it with an instance of SuperType. This means that all properties and methods accessible to SuperType instances will also exist in SubType.prototype. After this inheritance, the code immediately adds a new method to subType. prototype, the SuperType instance. Finally, an instance of SubType is created and its inherited getSuperValue() method is called.

Simulation of the new

What happens when YOU use new?

  1. Creates an empty object as an instance of the object to be returned
  2. Points the empty object’s prototype to the constructor’sprototypeattribute
  3. Assigns this empty object to the internalthisThe keyword
  4. Start executing the code inside the constructor
  5. If the constructor returns an object, the object is returned directly, otherwise the created object is returned

That is, inside the constructor, this refers to a newly generated empty object on which all operations will be performed. A constructor is called a “constructor” because its purpose is to manipulate an empty object (this object) and “construct” it into what it needs to look like.

function simulateNew() { let newObject = null,result = null, Constructor = Array. The prototype. The shift. The call (the arguments) / / parameter to judge the if (typeof constructor! Error ('type error') return} // Create an empty object, NewObject = object.create (constructive.prototype) // point this to a newObject, Result = constructive. apply(newObject, Judging the arguments) / / return objects const flag = result && (typeof result = = = 'object' | | typeof result = = = 'function') / / returns the result return flag ? result : */ function Person(name) {this.name = name} const p1 = new Person("p1") const p2 = simulateNew(Person, 'p2') console.log("p1",p1, p1 instanceof Person); console.log('p2', p2, p2 instanceof Person)Copy the code

Simulation instanceof

The main implementation principle of Instanceof is “as long as the right variable’s” prototype “is on the left variable’s prototype chain.” Thus, instanceof iterates through the prototype chain of the left variable until it finds the prototype of the right variable. If the lookup fails, it returns false, telling us that the left variable is not an instanceof the right variable.

function instanceOf(leftVaule, rightVaule) { let rightProto = rightVaule.prototype; // Take the prototype value of the right expression leftVaule = leftvaule.__proto__; While (true) {if (leftVaule === null) {return false; } if (leftVaule === rightProto) { return true; } leftVaule = leftVaule.__proto__ } }Copy the code

conclusion

  • To access a property of an Object, first look at itself, if not, access the Object’s __proto__, look up the prototype chain until you find Object.prototype.__proto__.

  • Each function has a prototype attribute that points to the function’s prototype object.

  • The __proto__ of all function prototype objects will point to Object.prototype.

  • The end of the prototype chain is Object.prototype.__proto__, which is null.

This is the end of JavaScript prototype chain sharing. If you have a deeper understanding of JavaScript prototype chain after reading this article, please give me a thumbs up to encourage 😄! If there is something wrong, I hope we can correct it and make progress together