Out class

  1. Class is the new foundational syntactic sugar in ES6 that ostensibly supports formal object-oriented programming, but still uses the concepts of stereotypes and constructors behind it

  2. Class Student{} creates instance Student

  3. A class is essentially a function. The class itself points to the constructor. So you can think of classes in ES6 as just another way of writing constructors

class Person {} 

console.log(typeof Person) // function
console.log(Person === Person.prototype.constructor) // true
Copy the code





Class constructor

The constructor keyword

  1. The constructor keyword tells the interpreter that this function should be called when using the new operator to create class instances. This constructor is automatically generated even if there is no constructor in the class


  1. There are differences in where the attributes of a class are defined
  • Properties defined in constructor can be called instance properties (that is, defined on this object). Each instance corresponds to a unique member object, which means that instance attributes are not shared across stereotypes
  • Properties declared outside constructor, including functions, that are defined on a stereotype, can be called stereotype properties or stereotype methods (defined on a class and seen in __proto__ instances).
 class Person {
    constructor(name,age){
      this.name = name
      this.age = age
      this.read = function read(){
        console.log(` reading `); }}speak(){
      console.log(I call `The ${this.name}`); }}let person = new Person('Lee'.22)
  console.log(person.hasOwnProperty("speak")) // false
  console.log(person.hasOwnProperty("read")) // true
  console.log(person.__proto__.read) // undefined
  console.log(person)
Copy the code


The constructor

  1. What happens after calling Person’s constructor with new?

Whenever new generates an instance, the constructor function is called, and the class generates the same function if it is not written

Then create a new object in memory — > person.__ proto __ = Person.prototype — > inside the constructor this points to the new object, and then execute the constructor code to add attributes to the new object — > return the new object


  1. The __ proto __ attribute of the class (the second step in the above procedure relates to this)
  • In fact, all instances of the class share a prototype object, and their prototype is Person.Prototype, so the proto property is equal
class Person {
    constructor(name,age){
      this.name = name
      this.age = age
      this.read = function read(){
        console.log(` reading `); }}speak(){
      console.log(I call `The ${this.name}`); }}let person = new Person('Lee'.22)

  // Person.__proto__ is person.prototype
  ƒ speak()
  console.log(person.__proto__)

  // Person {name: "LeeCopy", age: 23, read: ƒ}
  let person2 =  new person.__proto__.constructor('LeeCopy'.23)
  console.log(person2)
  
  // true
  console.log(person.__proto__ === person2.__proto__)
Copy the code

The knowledge involved in the prototype chain is shown below (borrowed from the Little Red Book)


  • Rewriting a stereotype with the proto attribute of an instance changes the original definition of the Class, affecting all instances
  class Person {
    constructor(name,age){
      this.name = name
      this.age = age
      this.read = function read(){
        console.log(` reading `); }}speak(){
      console.log(I call `The ${this.name}`); }}let person = new Person('Lee'.22)
  let person2 =  new Person('LeeCopy'.23)
  
  // Do not use the arrow function, because then this points to window
  person.__proto__.sayAge = function () {console.log(this.age)}
  person.__proto__.speak = null

  person2.sayAge() / / 23
  person2.speak() // Uncaught TypeError: person1.speak is not a function
Copy the code


Instance properties and stereotype properties

  • Properties defined in constructor can be called instance properties (that is, defined on this)
  • Properties declared outside constructor are defined on a stereotype and can be called stereotype properties (defined on a class).
  • The hasOwnProperty() function is used to determine whether an attribute is an instance attribute. The result is a Boolean value, true meaning instance property, false meaning not instance property.
  • The IN operator returns true if a given property is accessible through an object, whether it exists in an instance or stereotype
class Box {
    constructor(num1,num2) {
        this.num1=num1;
        this.num2=num2;
    }
    sum(){
        return this.num1+this.num2
    }
}

const box = new Box(12.88)
console.log(box.sum()) / / 100
console.log(box.hasOwnProperty('num1')) //true
console.log(box.hasOwnProperty('sum')) // false
console.log('num1' in box) // true
console.log('sum' in box)  //true
console.log('say' in box)  //false
Copy the code


The this in the constructor refers to the problem

This in constructor refers to the instance object, and this in a method defined in class non-constructor refers to the caller of this method

class Person {
    constructor(_name, _age) {
        // This points to the instance object
        this.name = _name
        this.age = _age
        this.btn = document.querySelector('button')
        this.btn.onclick = this.sing // Assign the onclick event attribute to the BTN element
    }

    sing() {
        // This in sing refers to BTN because it is the BTN button that calls the function
        console.log(this)}}Copy the code





Class inheritance

Class inheritance uses a new syntax, but it still uses a stereotype chain behind it. Using the extends keyword, you can inherit any object that has constructor and Prototype, which means you can inherit not only classes but also constructors

Syntax for class inheritance

  1. Subclasses must call the super method in the constructor method before they can use the this keyword

This is because subclasses don’t have their own This object, but instead inherit this from their parent class. If you don’t call super, your subclasses don’t get this. Thus, subclasses need to add super() to their constructor function before defining their own properties

  // error: Must call super constructor in derived class before accessing
  / / not super ()
  class Son extends Father {
    constructor(sonName, sonAge) {
      this.sonName = sonName;
      this.sonAge = sonAge
    }
  }
  
  // error: Must call super constructor in derived class before accessing
  // This precedes super(), when subclasses do not have their own this object
  class Son extends Father{
    constructor(sonName,sonAge,school) {
      this.school = school
      super(sonName,sonAge); }}Copy the code

ES5 inheritance is the opposite of ES6 in that it first creates its own This object and then adds the parent’s attribute methods to its this

// The child constructor calls the parent constructor with its own this
function Son(sonName,sonAge,school) {
       Father.call(this,sonName,sonAge)
       this.school = school
       this.sonSay = function (){
              console.log(`son`)}}Copy the code


  1. If a subclass does not explicitly define Constructor, the code is added by default. In other words, if the constructor function contains only super, the constructor function can be omitted
// If you do not want to add new attributes to subclasses, the following two methods have the same effect
  class Son extends Father{}

  class Son extends Father{
    constructor(sonName,sonAge) {
      super(sonName,sonAge); }}Copy the code


The proximity principle in inheritance (the subclass approach is preferred)

The first thing to know is that the super keyword can call either the constructor of the superclass or ordinary (or even static) functions of the superclass

class Father {
    constructor(name, age) {
      this.name = name;
      this.age = age;
    }
    sayMe() {
      console.log('I'm dad, my name isThe ${this.name}Age,The ${this.age}`)}}class Son extends Father{
    constructor(sonName,sonAge,school) {
      super(sonName,sonAge);
      this.school = school
    }
    sayMe() {
      console.log('I covered dad's way, I calledThe ${this.name}Age,The ${this.age}`)
      super.sayMe()
    }
}

let son = new Son('son of Lee'.23)
// I covered dad's method, my name is Lee son, age 23
// MY name is Lee Son. I am 23 years old
son.sayMe()
Copy the code


The nature of class inheritance

  1. ES6 class inheritance

Let’s take a look at inheritance with ES6’s class implementation

class Father {
    constructor(name, age) {
      this.name = name;
      this.age = age;
    }
    fatherSay() {
      console.log(`father`)}}class Son extends Father{
    constructor(sonName,sonAge,school) {
      super(sonName,sonAge);
      this.school = school
    }
    sonSay() {
      console.log(`son`)}}let son1 = new Son('son of Lee'.23.'the south mail')
  console.log(son1)
  // son1.__proto__ refers to Father instance, equivalent to son.prototype = new Father()
  // But son1.__proto__ constructor refers back to Son
  console.log(son1.__proto__)
Copy the code

We can clearly see that the ES6 class implementation inheritance still uses the ES5 prototype chain implementation inheritance, with slightly different details


  1. The ES5 constructor implements inheritance

Let’s take a look at how ES5 implements inheritance

// Parasitic combinatorial inheritance
function Father(name,age){
       this.name = name;
       this.age = age;
       this.fatherSay = function (){
             console.log(`father`)}}function Son(sonName,sonAge,school) {
    Father.call(this,sonName,sonAge)
    this.school = school
    this.sonSay = function (){
          console.log(`son`)
        }
}
Son.prototype = Object.create(Father.prototype)
Son.prototype.constructor = Son

let son1 = new Son('son of Lee'.23.'the south mail')
console.log(son1)
console.log(son1.__proto__)
console.log(son1.__proto__.constructor) // refer back to the constructor

// Parasitic combinatorial inheritance has advantages over combinatorial inheritance:
// the parent constructor is called only once, saving performance.
//2. Avoid generating unnecessary attributes.
// Type inheritance ensures that the context of the prototype chain is unchanged. The prototype of the subclass has only properties and methods declared by prototype, and the prototype of the parent class has only properties and methods declared by prototype

Copy the code

You can see the similarities between class inheritance and constructor inheritance

The difference is highlighted in the red line — constructor inheritance functions are instance properties or instance functions. Instance properties are not shared by stereotypes, but in inheritance, instance properties of the parent class become stereotype properties, which are reused by subclasses, and become instance properties of subclasses


  • Addendum: Prototype chain in inheritance

Prototype chain the basic idea is through the prototype of inheriting the properties and methods of multiple reference type, the prototype of the child is the parent instance, imply a prototype itself has an internal pointer to another prototype, the appropriate parent prototype also has a pointer to another constructor, such instances, and between the prototype is constructed a prototype chain.

Application of class inheritance

  • Inherits the built-in reference types and extends the built-in types
// randomArr can create an array with a built-in shuffle algorithm
  class randomArr extends Array {
    shuffle() {
      for (let i = this.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 2));
        [this[i], this[j]] = [this[j], this[i]]; }}}let arr = new randomArr(1.2.3.4.5)
  console.log(arr) // [1, 2, 3, 4, 5]
  arr.shuffle()
  console.log(arr) // [5, 4, 2, 3, 1]
  console.log(arr instanceof randomArr) // true
  console.log(arr instanceof Array) // true
Copy the code





Class static methods

Static method: A method that can be called directly from the class without instantiating it, with the static keyword in front of the prototype method

The characteristics of

  1. A method that can be called directly from the class and is not inherited by the instance
class Father {
    constructor(name, age) {
      this.name = name;
      this.age = age;
    }

    static fatherSay() {
      console.log(`father`)
    }
  }

  Father.fatherSay() // father

  let father = new Father('Lee'.23)
  // Uncaught TypeError: father.fatherSay is not a function
  father.fatherSay()
Copy the code


class Father {
    constructor(name, age) {
      this.name = name;
      this.age = age;
    }

    static fatherSay() {
      console.log(`father`)}static useStatic () {
      this.fatherSay()
    }
    
    failUseStatic (){
      this.fatherSay()
    }
  }

  Father.useStatic() // father

  let father = new Father('Lee'.23)
  // Uncaught TypeError: this.fatherSay is not a function
  father.failUseStatic()
Copy the code


  class Father {
    static fatherSay() {
      console.log(`father`)}}class Son extends Father {
    static sonSay() {
      super.fatherSay()
    }
  }

  Son.sonSay() // father
Copy the code

application

  • Static methods are well suited as instance factories
class Person {
    constructor(age) {
      this.age = age
    }
    static create () {
      return new Person(Math.floor(Math.random()*100))
    }
  }
  person1 = Person.create()
Copy the code