Step by step, the constructor, class, and prototype chain problems are resolved

Static member, instance member, static method, instance method

class Star{
  constructor(uname,age){
    // Instance member
    this.uname = uname
    this.age = age
    // Instance method
    this.say = function(){
      console.log('say')
    }
  }
}
Star.work = 'job'  // Static member
Star.dance = function(){ // Static method
  console.log('dance')}var star = new Star('zcl'.'22')
console.log(star)
console.log(star.work)  // undefined
console.log(Star.work)   // job 
star.dance()  / / an error
Star.dance()  // dance
Copy the code

star:

As you can see from the above image, instance members and instance methods are displayed in the created Star object, but the static members work and the static methods dance are not added to the star object. This shows that when you use the star object to access static members and methods, undefined and error are returned. Because static members and methods on class Star can only be called by the class, not by objects created by the class. Likewise, instance members and instance methods can only be called from the star object, not the star class.

function Father(name,age){
  this.name = name
  this.age = age
  this.sing = function(){
    console.log('sing')}}var son = new Father('zcl'.22)
console.log(son)
Copy the code

Similarly, create an object in the form of a constructor. The name,age, and sing in the constructor are also added to son, and cannot be accessed by the constructor itself.

Methods and members on Prototype

class Star{
  constructor(uname,age){
    this.uname = uname
    this.age = age
    this.say = function(){
      console.log('say')}}sing(){
    console.log('sing')
  }
}
Star.prototype.work = 'job'
Star.prototype.dance = function(){
  console.log('dance')}Copy the code

star:

We can see from the above image that the Star. Prototype member wrok and the dance method appear in the Star __proto__, and the Star class sing method also appears in the Star class. This means that the sing method is actually added to the object’s __proto__ in the same way that the dance method is. __proto__ is equivalent to start. prototype.

function Father(name,age){
  this.name = name
  this.age = age
  this.sing = function(){
    console.log('sing')
  }
}
Father.prototype.work = 'job'
Father.prototype.dance = function(){
  console.log('dance')}var son = new Father('zcl'.22)
Copy the code

The same is true with constructors, but constructors differ from classes in that classes can be directly displayed on objects by defining instance methods in construcotr, whereas constructors cannot, which can only be displayed on objects’ __proto__ by adding methods to their constructor’s prototype.

3. The prototype and __proto__

3.1 Dual identity

Why do we add attributes directly to a class or constructor that can only be accessed by the class or constructor itself?

And the reason for that is that whether it’s a class or a constructor first of all it’s a function and that’s unquestionable but what a function is is an object. So when we add a property to an object, of course we can only get that property from that object.

So for constructors and classes that have dual identities, what do their prototype and __proto__ refer to?

function Test(){
  this.a = 1
}
const test = new Test()
Copy the code
console.log(Test.prototype === test.__proto__) // The object's __proto__ holds the prototype of the object's constructor
console.log(Test.prototype.__proto__ === Object.prototype)  // Test. Prototype is also an object and therefore has a __proto__ attribute
console.log(Object.prototype.__proto__)  // null goes to the top layer and there is no __proto__ attribute
Copy the code

As a function, it constructs an object whose __proto__ is equal to its prototype. And __proto__ constructor records who constructed the object!!

console.log(Test.__proto__ === Function.prototype)  // True an object's __proto__ is equal to the prototype of the function that constructed its object
console.log(Function._proto_ === Function.prototype)  // The underlying specification because it constructs itself
Copy the code

As an object, an object’s __proto__ is equal to the prototype of the Function that constructed it

console.log(typeof Test)  // Function
console.log(Test instanceof Object) // true
Copy the code

3.2 Analyze the advantages and disadvantages of the four main methods of inheritance step by step

3.2.1 Use of prototype chain inheritance

function Animal(){
  this.colors = ['black'.'white']
  this.name = 'zcl'
}
Animal.prototype.getColor = function(){
  return this.colors
}

function Dog(){}
Dog.prototype  = new Animal()   // Inheritance through the prototype chain
let dog1 = new Dog()
console.log(dog1)
Copy the code

dog1:

Dog. Prototype = new Animal() = new Animal() = new Animal();

Animal’s __proto__ instance colors and name are attached to animal. prototype and getColor means __proto__ is attached to Animal.

function Animal(){
  this.colors = ['black'.'white']
  this.name = 'zcl'
}
Animal.prototype.getColor = function(){
  return this.colors
}
function Dog(){}
Dog.prototype  = new Animal()   // Inheritance through the prototype chain
let dog1 = new Dog()
dog1.colors.push('red')
dog1.name = 'zzr'
console.log(dog1)
Copy the code

dog1:

When we add a member variable to Dog’s instance object, that member variable is present in the instance object, but when we change the reference type, Dog’s instance object will follow the __proto__ chain to find the colors array and then modify it, so it won’t be present in the instance object.

function Animal(){
  this.colors = ['black'.'white']
  this.name = 'zcl'
}
Animal.prototype.getColor = function(){
  return this.colors
}

function Dog(){}
Dog.prototype  = new Animal()   // Inheritance through the prototype chain
let dog1 = new Dog()
dog1.colors.push('red')
dog1.name = 'zzr'
dog1.age = '23'
console.log(dog1)
let dog2 = new Dog()
console.log(dog2)
console.log(dog2.colors)  // We can see that dog2 has no colors operation but returns the result of dog1 colors operation
Copy the code

dog2:

When we use Dog instance object again, the name in DOG1 as its private property is not accessible by Dog2, so the name accessed by DOG2 is a member variable of the Animal instance object. And when DOG1 makes changes to the Colors array, it radiates to all subsequent instances built with Dog because of the colors changes on the prototype chain!

Output problems:

Dog. Prototype = new Animal(); constructor = new Animal(); This attribute should be actual Dog. The prototype. The constructor = Dog

2. All reference types on the stereotype chain, such as the colors attribute, will be shared by all instances

3. A subclass cannot take arguments to its parent constructor during instantiation

3.2.2 Borrowing constructor inheritance

function Animal(name){
  this.colors = ['white'.'red']
  this.name = name 
  this.getName = function(){
    return this.name
  }
}
function Dog(name){
  Animal.call(this,name)
}
Dog.prototype = new Animal('zcl')

let dog1 = new Dog('zzr')
dog1.colors.push('red')
console.log(dog1)
Copy the code

dog1:

Let’s walk through the steps that led to the above results:

  1. Prototype = new Animal(); / / Dog. Prototype = new Animal(); / / Dog. Prototype = new Animal

  2. Second, when we instantiate the Dog object we pass in a name argument but in the Dog constructor we call the Animal constructor using the call method and pass in the Dog instance and name. We run the Animal constructor again but this time it points to the Dog instance So the Dog instance has its own private attributes colors getName and Name

Problems:

  1. Every time we create an instance, we call the superclass constructor twice new Animal() and animal.Call (this,name)
  2. The Animal constructor defines a member method so we’re going to create this method every time we create an Animal instance

3.2.3 Combinatorial Inheritance

function Animal(name) {
  this.name = name
  this.colors = ['black'.'white']
}
Animal.prototype.getName = function() {
  return this.name
}
function Dog(name, age) {
  Animal.call(this, name)
  this.age = age
}
Dog.prototype =  new Animal('zcl')
Dog.prototype.constructor = Dog
let dog1 = new Dog('shake'.2)
dog1.colors.push('brown')
console.log(dog1)
Copy the code

dog1:

What this approach optimizes:

  1. We put member methods on animal.Prototype so that we don’t generate them every time we create an instance
  2. And we will t prototype. The constructor this attribute to expand out this constructor refers back to the Dog

3.2.4 Parasitic combination inheritance

function Animal(name) {
  this.name = name
  this.colors = ['black'.'white']
}
Animal.prototype.getName = function() {
  return this.name
}
function Dog(name, age) {
  Animal.call(this, name)
  this.age = age
}
Dog.prototype =  Object.create(Animal.prototype)
Dog.prototype.constructor = Dog
let dog1 = new Dog('shake'.2)
dog1.colors.push('brown')
console.log(dog1)
Copy the code

dog1:

Here we have solved the problem of calling Animal only once, where object. create is used to mount Aninam. Prototype to the __proto__ of Dog. So we can see that Dog. Prototype has no Animal member variable because there is no manipulation at this level!!

This step leads to another approach:

function Animal(name) {
  this.name = name
  this.colors = ['black'.'white']
}
Animal.prototype.getName = function() {
  return this.name
}
function Dog(name, age) {
  Animal.call(this, name)
  this.age = age
}
Dog.prototype =  Animal.prototype
Dog.prototype.constructor = Dog
let dog1 = new Dog('shake'.2)
dog1.colors.push('brown')
console.log(dog1)
Copy the code

Dog. Prototype = Animal. Prototype = Dog.

Some applications based on prototype chain

4.1 Implement the new keyword

function newFactory(){
  var obj = new Object()
  Constructor = Array.prototype.slice.call(arguments) [0]  // The constructor passed in
  arguments = Array.prototype.slice.call(arguments).slice(1)  // The member attribute passed in
  // person's prototype and person instance __proto__ have the Constructor attribute and other methods But now obj.__proto__ is empty
  obj.__proto__.constructor  = Constructor   // This is equivalent to the form above
  var ret = Constructor.apply(obj,arguments)  / / inheritance

  return typeof ret === 'object' ? ret || obj:obj  // Return this object
}
Copy the code

The constructor property is inside the __proto__ of the newly created instance. Second, we need to call the parent constructor and pass in the parameters, so we thought of using apply

4.2 Implementing the Instanceof keyword

The main function of instanceof is, in plain English, an instanceof B — whether or not A is created by B!

So obviously if we want to see if A is created by B that is, if A is an instance of B then we can say that the __proto__ of the instance object created by B is equal to the prototype of the constructor B.

function instanceOf(left,right){
  let proto = left.__proto__
  while(true) {if(proto === null) return false
    if(proto === right.prototype){
      return true
    }
    proto = proto.__proto__ // Continue down the prototype chain}}Copy the code

4.3 implementation Object. The create

The Object. Create function is to mount the existing Object on the __proto__ of the newly created Object.

Which object to mount from the __proto__ of the new object and which attribute to add (optional) to the new object


Object.createFunction = function(proto, propertyObject = undefined) {
  if (typeofproto ! = ='object' && typeofproto ! = ='function') {
      throw new TypeError('Object prototype show be an object')}if (propertyObject == null) {
      new TypeError('Cannot convert undefined or null to object')}function F() {}   // Create a new constructor
  F.prototype = proto  // The constructor's prototype is equivalent to obj's __proto__, making it equal to the passed argument one, which is an object
  const obj = new F()
  if(propertyObject ! =undefined) {
    // Handle attributes
      Object.defineProperties(obj, propertyObject)
  }
  if (proto === null) {   / / for the Object. The create (null)
      obj.__proto__ = null
  }
  return obj  // Returns a new object
}
Copy the code

Five Tips.

Please correct any errors in the above description