1. What is the prototype

 funetion Person(){}const p1 = new Person()
 
 Person.prototype  //Object
 Person.prototype ===  p1.__proto__ //true
 Person.prototype.__proto__ === Object.prototype //true
 
 Array.prototype / / []
 Array.prototype === []. _proto //true
 Array.prototype.__proto__ === Object.prototype //true
 
 Object.prototype //Object {}
 String.prototype //String (length; 0, [[Primitivevalue]l:"") 
 Function.prototype //function (){}
Copy the code

Each object instance has a _proto_ attribute, and each constructor has a prototype attribute

Note that the _proto_ attribute is not standard and is not currently compatible with all browsers

Constructor (); obj (); obj ();

constructor.prototype= = =obj._proto_
constructor.prototype._proto_= = =Object.prototype 
Object.prototype._proto_= = =null
Copy the code

Conclusion:

  • Each constructor’s Prototype points to a different, unique object
  • The constructor’s prototype property and the object instance’s _proto_ point to the same prototype object.
  • The top of the prototype chain for all objects points to object. prototype, which points to NULL.
  • The prototype of any constructor (or _proto_ of any object instance) is an object instance of 0bject

2. Obtain object attributes

Set obj to an instance of an object, execute console.log(obj.foo), and what happens:

When getting (or referencing) an attribute of an object instance, it checks whether the object instance itself contains the attribute directly and uses it if it does (that is, the GET operation). If not, the prototype chain is traversed, and if it still cannot be found, undefined is returned

3. Set object properties

Set obj as an instance of an object and call obj.foo = ‘bar’. What happens?

  • If an obj object directly contains an attribute named foo, the existing direct attribute value is modified

  • If an obj object does not directly contain an attribute named foo, its prototype chain is traversed, and if an attribute named foo is still not found, foo is added to obj’s directly contained attribute

  • Following that, foo is added to obj’s direct include property if a corresponding foo property exists when iterating through its prototype chain and that property is writable:true. That is, the corresponding attributes on the prototype chain are shielded

  • If a corresponding property foo exists while iterating through its prototype chain and the property is read-only (writable:false), neither the property is modified nor a direct property of the object instance is created, and nothing happens (an error is thrown in non-strict mode).

  • Following that, if there is a corresponding property of Foo when iterating through its prototype chain, and that property is a setter, that setter must be called. It is not added as a direct include property of obj at this time

4. Confuse the constructor attribute

 function Person(){}const p1 - new Person()
 
 Person.prototype.constructor === Person //true
 p1.constructor === Person //true
Copy the code

In the code above, we see that the prototype constructor has a constructor attribute by default, and the object instance created has a constructor attribute by default. They all refer uniformly to the constructor itself, which “seems” to indicate that the property refers to “the function that created the object” or “the object instance is created by… Create”

In fact, the object instance itself does not have a constructor attribute, and this attribute well does not indicate that… Structure”

First we need to dispel a common misconception:

All function declarations (whether or not they start with a capital letter) are ordinary functions. Functions themselves are not constructors, and function calls become “constructor calls” if and only if you use new.

Using the above example, let’s unmask the constructor attribute:

  • The Constructor property of Person.prototype is just the default property when the Person function is declared; it points to the Person itself by default.
  • Object instances generated when the new Person can access the constructor property is based on the prototype chain access to the Person. The prototype. The constructor.
  • If we change the reference to Person.prototype, its corresponding constructor will not retain the original value, i.e., it will no longer be Person.

See the example below:

 function Person() {}

 const p1 - new Person()
 p1.constructor === Person //true
// Modify the constructor's prototype object
 Person.prototype - new Array(a)// Create a new instance
 const p2 = new Person()
 
// Cantructor's direction has changed
p2.constructor === Person //false
p2.constructor === Array//true
Copy the code

Code parsing:

  • Start by creating an object instance with new Person()p1.constructor, it has the property itself, so it is accessed according to the prototype chainPerson.prototype(i.e.p1.__proto__), because Person is declared asPerson.prototypeThis property is available by default, so it is accessed. Its value points to Person.
  • And then we modify itPerson.prototypeGenerate an object instance P2 for an image instance of the Array constructor, followed by new Person. At this time to visitp2.constructor. Similarly, P2 itself does not have this property and is then accessed along the prototype chainPerson.prototypeAt this point it has been modified so that the original constructor value is lost. So we continue to follow the prototype chainPerson.prototype.__proto__, i.e.,new Array().__proto__, that is,Array.prototype. And similarly with Person,Array.prototype.constructorThe default is to point to the constructor Array. So calling p2.constructor points to Array

If the Construtor attribute is represented by… Constructor, then the constructor property of the object instance of Person should always point to Person, which is clearly not the case.

5. Prototype inheritance

From the knowledge of prototype chain, we know that the new instance object can access the properties or methods in the prototype object. Sometimes we need to change the default orientation of the prototype object to get its properties and methods. This is called archetypal inheritance.

If Bar and Foo are two different constructors and we want Bar to “stereotype” Foo, there are several main ways to change the stereotype object:

(1) Bar.prototype === Foo.prototype

This method simply changes the bar. prototype reference from the default prototype object to foo. prototype. The problem with it is if you modify one of the prototype objects. It affects another prototype object, so that’s not what we want

(2)Bar.prototype === new Foo()

Compared with (2), this approach solves the problem of shared prototype object references, but if Foo has some side effects (such as changing state, adding attributes to this, etc.), it also affects Bar instance objects and is not desirable

(3)Bar.prototype === 0bject.create(Foo.prototype)

Object.create (obj) solves problems (1) and (2) by creating and returning a new object out of thin air that is modeled after the target parameter object.

The only drawback is that this method abandons the default prototype object and creates a new object in its place. There is a slight performance loss.

(4)Object.setPrototypeof(Bar.prototype,Foo.prototype)

This method is new to ES6 and has the advantage over (3) in that it directly modiates the reference to the object of the prototype without the performance problems of garbage collection caused by discarking the object