Before summarizing ES6 Class, I think it is necessary to sort out the knowledge of ES5 first. This will make it easier to compare and remember.

Factory Pattern

The factory pattern encapsulates the process of creating an object using a function. Example:

var Animal = function(name="动物"){
    var animal = new Object();
    animal.name=name
    animal.eat = function(){
        console.log("吃食物")
    }
    return animal
}
var dog = Animal("狗") 

Although it solves the problem of creating multiple similar objects, it does not know the type of the created object, and each time an object is created, a new function is generated and there is no reuse.

Constructor Pattern

What happens after the constructor is called is:

  1. Create a new object
  2. Assign the constructor’s scope to the new object (this refers to the new object)
  3. Execute the code in the constructor (add properties to the object)
  4. Return the new object

The code expression is:

var Animal = function(name="动物"){
    this.name = name;
    this.eat = function(){
        console.log("吃食物")
    }

}
function newAnimal() {
    // The following is the code generated by calling the constructor
    var obj = {};
    // Without this step, dog1 instanceof Animal will be false
    obj.__proto__ = Animal.prototype;
    Animal.call(obj)
    return obj
}    
var dog1=  newAnimal()
console.log(dog1 instanceof Animal)

As you can see, by pointing the instance’s __proto__ to the prototype object of Animal, Animal can be turned into the instance’s constructor, which will be discussed later.

Now rewrite the previous example using the constructor pattern.

var Animal = function(name="动物"){
    this.name = name;
    this.eat = function(){
        console.log("吃食物")
    }
}
var dog = new Animal("狗") 
console.log(dog.constructor==Animal)//true

The constructor attribute of the instance object generated by the constructor points to its constructor.

The instanceof operator is used to detect the type of an object.

dog instanceof Animal //true
dog instanceof Object // true (all objects inherit from Object)

Since Animal is a constructor, it can be run just like a normal function. Let’s see what happens if we execute it directly without using the new operator:

var Animal = function(name="动物"){
    this.name = name;
    this.food = "食物1"
    this.eat = function(){
        console.log("吃食物")
    }
}
Animal()
console.log(food)// "Food 1"
eat()// Eat food

You can see that the properties and methods of Animal are added to the global object because when calling a function in the global scope, this always points to Globol

Don’t forget, usually, this inside a function called by a non-object always points to the global

The constructor is a slight improvement over the factory pattern, but it does not solve the problem of function reuse. Let’s verify it as follows:

var Animal = function(name="动物"){
    this.name = name;
    this.food = "食物1"
    this.eat = function(){
        console.log("吃食物")
    }
}
var dog1= new Animal()
var dog2 = new Animal()
console.log(dog1.eat ==dog2.eat)//false    

Obviously, for an action like eating food, I don’t need two functions to handle it. So…

function Animal(){
    /.../
}
function eat(){
    console.log("吃食物")
}

However, such globally defined functions are easily tampered with, so it is not practical.

Prototype Pattern

Every function we create has a prototype property that points to a prototype object. The prototype object contains properties and methods shared by instances created by the constructor.

This resolves all instances sharing the function.

Change the above to prototype mode:

Example 1:

function  Animal(){

}
Animal.prototype = {
  name : "动物",
  food : ["食物1","食物2"],
  eat(){
    console.log(this.foods)
  }
}

var dog = new Animal();
var cat = new Animal() 

console.log(dog.eat == cat.eat)//true
console.log(dog.name == cat.name)//true
console.log(dog.food == cat.food)//true

As you can see, dog and cat share the same eat function. So what exactly does the prototype object do?

Prototype Object

Whenever a new function is created, it will have a prototype property that points to the prototype object. By default, the prototype object will automatically have a constructor property (so that instances can also use this property), which points to the constructor function.

That is to say

Animal.prototype.constructor points to Animal itself

It also means that dog.__proto__.constructor==Animal points to the constructor Animal

Then use the previous example 1 to experiment

console.log(dog.__proto__.constructor==Animal)//false
console.log(Animal.prototype.constructor===Animal)//false

I found that this was not the case, because I had overwritten the prototype object. I will talk about this later.

After creating a constructor, its prototype object will only obtain the constructor property by default, and other methods are inherited from Object.

When the constructor is called to create an instance, the instance will contain a pointer to the prototype object of the constructor [[prototype]]. There is no standard way to access it in Js. In some browsers, __proto__ can be used to access it.

The relationship between the instance and the prototype object can be tested using isPrototypeOf()

Animal.prototype.isPrototypeOf(dog)//true

The previous problem only involves the relationship between the prototype and the constructor, and does not affect the relationship between the prototype and the instance.

There is a more recommended method to test

Object.getPrototypeOf(dog)===Animal.prototype//true
```**When the code reads a property of an instance object, it searches for the named property, starting with the object instance itself, and then continuing to its prototype object if it doesn't find the named property.**

If we add a property to an instance with the same name as a prototype, the property will be created on the instance, obscuring the prototype's property.
The `hasOwnProperty()` method can be used to determine whether the prototype property or the instance property is being accessed.

The for in operator iterates over properties in both instances and prototypes.

##### Problems with prototypal inheritance

Back to the two questions above

Let me talk about the second one first.```

console.log(dog.__proto__.constructor==Animal)//false
console.log(Animal.prototype.constructor===Animal)//false

The instance prototype’s constructor no longer points to the constructor```

function Animal(){

}
Animal.prototype = {
  name : "动物",
  food : ["食物1","食物2"],
  eat(){
    console.log(this.foods)
  }
}

var dog = new Animal();

The reason is that after the constructor is created, the prototype of the constructor has a `constructor` property, but we later rewrite this prototype in the form of an object literal, resulting in the loss of the `constructor` property (`constructor` is not inherited from `Object`).

The solution is

```javascript
Animal.prototype = {
          constructor : Animal, 
          name : "动物",
          food : ["食物1","食物2"],
          eat(){
            console.log(this.foods)
          }
        }

Of course, not resolving this issue will not have any other impact on the instance. This problem is considered resolved.

Back to the first question

function  Animal(){

}
Animal.prototype = {
  name : "动物",
  food : ["食物1","食物2"],
  eat(){
    console.log(this.foods)
  }
}

var dog = new Animal();
var cat = new Animal() 

console.log(dog.eat == cat.eat)//true
console.log(dog.name == cat.name)//true
console.log(dog.food == cat.food)//true
`

dog.foodandcat.food` obviously share an object on the prototype, and it is a reference type. Then modifying this food in the instance will cause changes in food in other instances.

dog.food.push("火腿")

console.log(cat.food)// ["food 1", "food 2", "ham"]

Obviously, if we don’t want the ham to become cat food, we should take the attribute out separately instead of putting it in the prototype chain.

This requirement can be solved with a constructor

Combining the Constructor and Prototype Patterns

Since I have said almost everything before, let me just give you an example.

function  Animal(name="动物"){
  this.name = name
  this.foods = []
}
Animal.prototype = {
  constructor:Animal,
  eat(){
    console.log(this.foods)
  }
}

var dog = new Animal("哈士奇");
var cat = new Animal("橘猫") 
dog.foods.push("火腿")
cat.foods.push("鱼")

console.log(dog.name)// "Husky"
console.log(cat.name)// "Orange Cat"

dog.eat()// ["Ham"]
cat.eat()// ["fish"]

console.log(dog.hasOwnProperty("name"))//true
console.log(dog.constructor.name)// Animal (note that this is related to the function declaration method)
console.log(dog instanceof Animal)//true
Object.getPrototypeOf(dog)===Animal.prototype//true
```**To summarize, when creating an object using the constructor and prototype combination pattern, the object's properties are equivalent to deep copies from the constructor, while the methods are equivalent to shallow copies from the prototype. **