Origin one.

Prior to ES6, instance objects were generated in the form of constructors, as shown below

  function Point(x, y) {
    this.x = x;
    this.y = y;
  }
  Point.prototype.toString = function () {
    return '(' + this.x + ', ' + this.y + ')';
  };
  var p = new Point(1, 2);

Copy the code

But with ES6, the concept of Class was introduced. With the class keyword, you can define a class. Basically, ES6 class can be seen as a syntax candy, most of its functions can be done in ES5, the above code using ES6 class rewrite, that is, the following.

class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; }}Copy the code

The above code defines a “class”. You can see that it contains a constructor method, which is the constructor function. The this keyword represents the instance object, since this is bound to the class instance when new is used to create an instance of the class.

In addition to the constructor, the Point class defines a toString method. All methods of a class are defined on the prototype property of the class

class Point { constructor() { // ... } toString() { // ... } toValue() { // ... }} / / equivalent Point. The prototype = {constructor () {}, the toString () {}, toValue () {},};Copy the code

Note: Calling a method on an instance of a class is actually calling a method on the prototype. The indirect proof that point is a function is that the object has no prototype property.

Instances of the class

As in ES5, the attributes of an instance are defined on the stereotype (class) unless they are explicitly defined on themselves (that is, on this object).

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}
var point = new Point(2, 3);
point.toString() // (2, 3)
point.hasOwnProperty('toString') // false
point.__proto__.hasOwnProperty('toString') // true
Copy the code

In the above code, both x and y are properties of the instance object point itself (as defined on this), so hasOwnProperty returns true, ToString is a property of the prototype object (because it is defined on the Point class, all methods of the class are defined on the prototype property), so the hasOwnProperty method returns false. These are consistent with ES5 behavior.

As with ES5, all instances of a class share a stereotype object.

Var p1 = new Point(2,3); Var p2 = new Point(3,2); p1.__proto__ === p2.__proto__//trueCopy the code

Getters and setters

As in ES5, you can use the get and set keywords inside a “class” to set the store and value functions for an attribute and intercept the access behavior of that attribute.

Constructor () {//... } get prop() { return 'getter'; } set prop(value) { console.log('setter: '+value); } } let inst = new MyClass(); inst.prop = 123; // setter: 123 inst.prop// 'getter'Copy the code

This point

Class methods that contain this inside point to instances of the class by default. You must be careful, however, that you may get an error if you use this method alone.

class Logger {
  printName(name = 'there') {
    this.print(`Hello ${name}`);
  }
  print(text) {
    console.log(text);
  }
}
const logger = new Logger();
const { printName } = logger;
printName(); // TypeError: Cannot read property 'print' of undefined
Copy the code

In the code above, this in the printName method points to an instance of the Logger class by default. However, if we extract this method and use it alone, this will refer to the environment in which the method was run (because the class is in strict mode, so this actually refers to undefined), and we will fail to find print.

An easy solution is to bind this to the constructor so that the print method won’t be missing.

class Logger {
  constructor() {
    this.printName = this.printName.bind(this);
  }
  // ...
}
Copy the code

Another solution is to use arrow functions.

class Obj {
  constructor() {
    this.getThis = () => this;
  }
}
const myObj = new Obj();
myObj.getThis() === myObj // true
Copy the code

The this inside the arrow function always points to the object at which it was defined. In the above code, the arrow function is inside the constructor, and its definition takes effect when the constructor executes. In this case, the arrow function must be running in an instance object, so this will always point to an instance object.

Static methods and static properties

A class is the prototype of an instance, and all methods defined in a class are inherited by the instance. If you prefix a method with the static keyword, it means that the method is not inherited by the instance, but is called directly from the class. This is called a “static method”.

class Foo {
  static classMethod() {
    return 'hello';
  }
}
Foo.classMethod() // 'hello'
var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function
Copy the code

Note that if a static method contains the this keyword, this refers to the class, not the instance.

Static methods of a parent class that can be inherited by subclasses. class Foo { static classMethod() { return ‘hello’; }} class Bar extends Foo {} bar.classmethod () // ‘hello’ In the above code, the parent Foo class has a static method that the subclass Bar can call.

Static methods can also be called from super objects.

class Foo {
  static classMethod() {
    return 'hello';
  }
}
class Bar extends Foo {
  static classMethod() {
    return super.classMethod() + ', too';
  }
}
Bar.classMethod() // "hello, too"
Copy the code

Static property writing

class Foo { // ... } Foo.prop = 1; Class Foo {static prop = 1; }Copy the code

Private methods and private properties

Private methods and properties are methods and properties that can only be accessed inside a class, not outside it. This is a common requirement that facilitates code encapsulation, but ES6 does not provide it and can only be simulated through workarounds. Private properties and methods can also be preceded by the static keyword to indicate that this is a static private property or method.

class FakeMath { static PI = 22 / 7; static #totallyRandomNumber = 4; static #computeRandomNumber() { return FakeMath.#totallyRandomNumber; } static random() {console.log('I heard you like random numbers... ') return FakeMath.#computeRandomNumber(); }} FakeMath.PI // 3.142857142857143 FakeMath. Random () // I heard you like random numbers... FakeMath.#computeRandomNumber()Copy the code

In the above code, #totallyRandomNumber is a private property and #computeRandomNumber() is a private method that can only be called inside the FakeMath class.

4. New way to write instance attributes (want to see how the principle is implemented?)

Instance attributes can be defined at the top level of the class, in addition to above this in the constructor() method.

class IncreasingCounter { constructor() { this._count = 0; } get value() { console.log('Getting the current value! '); return this._count; } increment() { this._count++; }} In the above code, the instance attribute this._count is defined in the constructor() method. Another way to write this is that this property can also be defined at the top level of the class, all else being the same. class IncreasingCounter { _count = 0; get value() { console.log('Getting the current value! '); return this._count; } increment() { this._count++; }}Copy the code

In the code above, the instance attribute _count is at the same level as the value() and increment() methods. At this point, you do not need to prefix the instance attribute with this.

The nice thing about this new approach is that all the attributes of the instance object itself are defined in the header of the class, so you can see at a glance what instance attributes the class has.

class foo { bar = 'hello'; baz = 'world'; constructor() { // ... }}Copy the code

As you can see at a glance from the above code, class Foo has two instance attributes. In addition, it is relatively simple to write.

5. Inheritance

Class Human{constructor(name){this.name = name} run(){console.log()}} Class Man extends Human{constructor(name){console.log()}} Constructor (name){super(name) this. Sex =' male '} habit(){console.Copy the code

Use constructor to write a class’s own attribute. The common attribute run (on the prototype chain) is written directly side by side with it

The way to write inheritance is: class is followed by a subclass and extends is your parent class

Man extends Human // equivalent to man.prototype.__proto__ = Human. PrototypeCopy the code

Then super(name) is equivalent to human.call (this,name), super is called Human, and call is called immediately.

The super keyword can be used as either a function or an object. In both cases, it’s used quite differently.

In the first case, super, when called as a function, represents the constructor of the parent class. ES6 requires that the constructor of a subclass must execute the super function once.

In the second case, super as an object, in a normal method, points to a prototype object of the parent class; In static methods, point to the parent class.

class A {
    p() {
      return 2;
    }
  }
  class B extends A {
    constructor() {
      super();
      console.log(super.p()); // 2
    }
  }
  let b = new B();
Copy the code

In the code above, super.p() in subclass B uses super as an object. In this case, super refers to a.prototype in normal methods, so super.p() equals a.prototype.p ().

It is important to note that since super refers to the parent’s prototype object, methods or properties defined on the parent instance cannot be called by super.

class A {
    constructor() {
      this.p = 2;
    }
  }
  class B extends A {
    get m() {
      return super.p;
    }
  }
  let b = new B();
  b.m // undefined
Copy the code

In the code above, p is an attribute of an instance of superclass A. super.p does not refer to it.

Super can be fetched if the property is defined on a stereotype object of the parent class.

class A {}
A.prototype.x = 2;
class B extends A {
  constructor() {
    super();
    console.log(super.x) // 2
  }
}
let b = new B();
Copy the code

ES6 states that when a method of a parent class is called through super in a subclass normal method, this inside the method refers to the current subclass instance.


class A {
  constructor() {
    this.x = 1;
  }
  print() {
    console.log(this.x);
  }
}
class B extends A {
  constructor() {
    super();
    this.x = 2;
  }
  m() {
    super.print();
  }
}
let b = new B();
b.m() // 2
Copy the code

In the code above, super.print() calls a.prototype.print (), but the this inside a.prototype.print () points to an instance of subclass B, resulting in 2 instead of 1. That is, super.print.call(this) is actually called

If super is used as an object in a static method, then super refers to the parent class, not the parent class’s prototype object.

class Parent {
  static myMethod(msg) {
    console.log('static', msg);
  }
  myMethod(msg) {
    console.log('instance', msg);
  }
}
class Child extends Parent {
  static myMethod(msg) {
    super.myMethod(msg);
  }
  myMethod(msg) {
    super.myMethod(msg);
  }
}
Child.myMethod(1); // static 1
var child = new Child();
child.myMethod(2); // instance 2
Copy the code

In the code above, super refers to the parent class in static methods and to the parent class’s prototype object in normal methods.

Also, when a method of a parent class is called through super in a static method of a subclass, the this inside the method refers to the current subclass, not the instance of the subclass.

class A {
  constructor() {
    this.x = 1;
  }
  static print() {
    console.log(this.x);
  }
}
class B extends A {
  constructor() {
    super();
    this.x = 2;
  }
  static m() {
    super.print();
  }
}
B.x = 3;
B.m() // 3
Copy the code

In the code above, in the static method B.m, super.print points to the static method of the parent class. This in this method refers to B, not an instance of B.

Note that when you use super, you must explicitly specify whether to use it as a function or as an object, otherwise an error will be reported.

class A {} class B extends A { constructor() { super(); console.log(super); // Error}}Copy the code

In the code above, the super in console.log(super) is not clear if it is used as a function or as an object, so the JavaScript engine will report an error when parsing the code. At this point, if the data type of super is clearly stated, no errors will be reported.

class A {}
class B extends A {
  constructor() {
    super();
    console.log(super.valueOf() instanceof B); // true
  }
}
let b = new B();
Copy the code

In the code above, super.valueof () indicates that super is an object, so no error is reported. Also, since super makes this refer to an instance of B, super.valueof () returns an instance of B.

Finally, since objects always inherit from other objects, the super keyword can be used in any object.

var obj = { toString() { return "MyObject: " + super.toString(); }}; obj.toString(); // MyObject: [object Object]Copy the code