Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

Creating classes using constructors is not only too similar to writing ordinary functions, but the code is not easy to understand

In ES6 (ECMAScript2015) the new standard uses the class keyword to define classes directly

But classes are essentially just syntactic sugar for a chain of constructors and prototypes

The creation of a class

Classes can be declared in two ways: class declarations and class expressions

Class declaration

class Person {}
Copy the code

Such expressions

const Person = class {}
Copy the code

The characteristics of the class

class Person {}

console.log(Person.prototype) / / = > {}
console.log(typeof Person) // => function

const per = new Person()

console.log(per.__proto__  === Person.prototype) // => true
Copy the code

Class constructor

When we use constructors to create instances, we often need to pass in parameters

In a class, the declaration of the class is separated from the constructor of the class

Each class can have its own constructor (method), whose name is the fixed constructor

When we operate on a class using the new operator, the class’s constructor is called

If we do not display the constructor that declares the class, the following default constructor exists

constructor() {}
Copy the code

When we declare our own constructor, we override the default constructor and start using our own constructor

Each class can have only one constructor, and if it contains multiple constructors, an exception is thrown

The constructor does the same thing you do when you create a function using the ES5 constructor

  1. Create a new object in memory (empty object)
  2. The [[prototype]] property inside this object will be assigned to the prototype property of the class
  3. The this inside the constructor points to the new object being created
  4. Internal code to execute the constructor (function body code)
  5. If the constructor does not return a non-empty object, the new object created is returned
class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
}

const per = new Person('Klaus'.23)
console.log(per) // => { name: 'Klaus', age: 23 }
Copy the code

Methods of a class

Instance methods

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

  // The methods defined here are methods on instances of the class
  // These methods are defined on the class's prototype
  running() {
    console.log('running')}}const per = new Person('Klaus'.23)

per.running() // => running
Person.prototype.running() // => running
Copy the code

Accessor method

class Person {
  // Define private attributes
  #address = 'Shanghai'

  constructor(name, age) {
    this.name = name
    this.age = age
  }

  // Accessors -- get
  get address() {
    return this.#address
  }

  // Accessors -- Settings
  set address(v) {
    this.#address = v
  }
}

const per = new Person('Klaus'.23)

console.log(per.address) // => Shanghai
per.address = 'Guangzhou'
console.log(per.address) // => Guangzhou
Copy the code

A static method

The static methods of a class are also called class methods

Unlike instance methods and accessor methods of a class, static methods of a class are defined on the class itself

You can only call from a class, not an instance of a class

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

  static running() {
    console.log('running')}}const per = new Person('Klaus'.23)

Person.running() // => runnning
per.running() // error
Copy the code

inheritance

The use of the extends keyword has been added to ES6 to facilitate inheritance

// Superclass
class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }

  eatting() {
    console.log('eatting')}static running() {
    console.log('running')}}// Subclass -- derived class
class Student extends Person {
  // If a subclass does not define a constructor, a default constructor exists
  // This constructor automatically calls the parent constructor for initialization
  /* constructor() { super(... arguments) } */
}

const stu = new Student('Klaus'.23)

console.log(stu.name, stu.age) // => Klaus, 23

// Subclasses inherit instance methods from their parent class
stu.eatting() // => eatting

// Subclasses can also inherit static and accessor methods from their parent class
Student.running() // => running
Copy the code
class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }

  eatting() {
    console.log('eatting')}static running() {
    console.log('running')}}class Student extends Person {
  constructor(name, age, sno) {
    // Note: Before you can use this in the constructor of a child (derived) class or return the default object (created instance object), you must call the constructor of the parent class through super
    super(name, age)
    this.sno = sno
  }
}

const stu = new Student('Klaus'.23.1810166)

console.log(stu.name, stu.age, stu.sno) // => Klaus, 23, 1810166
Copy the code

rewrite

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

  eatting() {
    console.log('eatting')}static running() {
    console.log('running')}}class Student extends Person {
  constructor(name, age, sno) {
    super(name, age)
    this.sno = sno
  }

  eatting() {
    console.log('student eatting')}static running() {
    console.log('student running')}}const stu = new Student('Klaus'.23.1810166)

stu.eatting() // => student eatting

Student.running() // => student running
Copy the code
class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }

  eatting() {
    console.log('eatting')}static running() {
    console.log('running')}}class Student extends Person {
  constructor(name, age, sno) {
    super(name, age)
    this.sno = sno
  }

  eatting() {
    // You can use the super keyword to call the corresponding methods (instance methods and static methods) in the subclass.
    super.eatting()
    console.log('student eatting')}static running() {
    super.running()
    console.log('student running')}}const stu = new Student('Klaus'.23.1810166)

stu.eatting()
/* => eatting student eatting */

Student.running()
/* => eatting student eatting */
Copy the code

Inherits from built-in classes

Object is the parent of all classes in JS

class Person {}
/ / equivalent to the
class Person extends Obejct {}
// If the parent class is not specified in JS, the parent class is set to Object by default
// The equivalent of extends Obejct is omitted and is the default
Copy the code

So we can also inherit from built-in classes

class CustomArray extends Array {
  getFirstItem() {
    return this[0]}getLastItem() {
    return this[this.length - 1]}}const customArr = new CustomArray(1.2.3)
console.log(customArr.getFirstItem()) / / = > 1
console.log(customArr.getLastItem()) / / = > 3
Copy the code

The class into

JavaScript classes only support single inheritance: that is, they can have only one parent class, but you want to emulate the effects of multiple inheritance in JS

That is, when you inherit a class and then use another method, you can use class mixing

JS itself does not realize the mixing of classes, but it can simulate the mixing of classes through the characteristics of JS functional programming

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

// Methods that need to be mixed in
function running() {
  console.log('running')}// Simulate mixin
function mixins(className, mixins) {
  class DeliverClass extends Person {}

  for (let k in mixins) {
    DeliverClass.prototype[k] = mixins[k]
  }

  return DeliverClass
}

const mixinPerson = mixins('Person', {
  running
})

const per = new mixinPerson('Klaus'.23)
console.log(per.name)
console.log(per.age)
per.running()
Copy the code

polymorphism

Polymorphism is the embodiment of the same operation for different data types and different behaviors

That is, variables of different data types call the same function, resulting in different execution effects

// Traditional object-oriented polymorphism has three premises:
// 1> must have inheritance (inheritance is a prerequisite for polymorphism)
// 2> Must have overrides (methods for subclasses to overwrite their parents)
// 3> There must be a parent class reference to a subclass object (variables of subclass type are assigned to variables of subclass type)

/ / parent class
class Shape {
  getArea(){}}/ / subclass
class Rectangle extends Shape {
  // Override the parent method
  getArea() {
    console.log('getArea in Rectangle')}}class Cricle extends Shape {
  getArea() {
    console.log('getArea in Cricle')}}// A parent class reference points to a subclass object
const r: Shape = new Rectangle()
const c: Shape = new Cricle()

r.getArea() // => getArea in Rectangle
c.getArea() // => getArea in Cricle
Copy the code

However, in JS, there are no restrictions on polymorphism as in traditional object-oriented languages

const o = {
  getArea() {
    console.log('getArea in o')}}function Person() {}
Person.prototype.getArea = () = > console.log('getArea in Person')
const per = new Person()

o.getArea() // => getArea in o
per.getArea() // => getArea in Person
Copy the code
function sum(a, b) {
  console.log(a + b)
}

sum(2.3) / / = > 5
sum('abc'.'cba') // => abccba
Copy the code