Class basic syntax

The traditional approach in JavaScript is to define and generate new objects through constructors. Here’s an example.

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

This is very different from traditional object-oriented languages such as C++ and Java, and can easily confuse new programmers. ES6 provides a more traditional language approach, introducing the concept of classes as templates for objects. With the class keyword, you can define a class. Such as:

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, and the this keyword represents the instance object. That is, ES5’s Point constructor corresponds to ES6’s Point class constructor.

When used, the new command is used directly on the class, exactly as the constructor is used.

The prototype property of the constructor continues on the ES6 “class”. In fact, all methods of a class are defined on the prototype property of the class.

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

Since the class’s methods are defined on top of the Prototype object, new methods of the class can be added to the Prototype object. The object. assign method makes it easy to add multiple methods to a class at once.

class Point {
  constructor(){
    // ...
  }
}

Object.assign(Point.prototype, {
  toString(){},
  toValue(){}
});
Copy the code

In addition, all methods defined inside a class are non-enumerable.

ES5:

var Point = function (x, y) {
  // ...
};

Point.prototype.toString = function() {
  // ...
};

Object.keys(Point.prototype)
// ["toString"]
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
Copy the code

Class class:

class Point {
  constructor(x, y) {
    // ...
  }

  toString() {
    // ...
  }
}

Object.keys(Point.prototype)
// []
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
Copy the code

The constructor method

The constructor method is the default method of the class and is automatically called when an object instance is generated using the new command. A class must have constructor methods; if not explicitly defined, an empty constructor() {} method is added by default.

The constructor method returns the instance object (that is, this) by default; you can specify to return another object at all. Such as:

class Foo {
  constructor() {
    return Object.create(null);
  }
}

new Foo() instanceof Foo
// false
Copy the code

Class constructor, which cannot be called without using new, will report an error. This is a major difference from normal constructors, which can be executed without new.

class Foo {
  constructor() {
    return Object.create(null);
  }
}

Foo()
// TypeError: Class constructor Foo cannot be invoked without 'new'
Copy the code

Class

Instance objects of generated classes are written exactly as in ES5, using the new command. If you forget to add new and call Class as a function, an error will be reported.

Var point = point (2, 3); Var point = new point (2, 3);Copy the code

As in ES5, the attributes of an instance are defined on a stereotype (that is, on a class) unless they are explicitly defined on themselves (that is, on this object). All instances of a class share a stereotype object, and methods can be added to the class through the __proto__ attribute of the instance.

There is no variable promotion

Class does not have a hoist, unlike ES5.

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 Log {
  printName(name = 'there') {
    this.print(`Hello ${name}`);
  }

  print(text) {
    console.log(text);
  }
}

const log = new Log();
const { printName } = log;
printName(); // TypeError: Cannot read property 'print' of undefined
Copy the code

The solution

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 Logger {
  constructor() {
    this.printName = (name = 'there') => {
      this.print(`Hello ${name}`);
    };
  }

  // ...
}
Copy the code

Another solution is to use a Proxy that automatically binds this when retrieving a method

function selfish (target) { const cache = new WeakMap(); const handler = { get (target, key) { const value = Reflect.get(target, key); if (typeof value ! == 'function') { return value; } if (! cache.has(value)) { cache.set(value, value.bind(target)); } return cache.get(value); }}; const proxy = new Proxy(target, handler); return proxy; } const logger = selfish(new Logger());Copy the code

summary

The class class is syntactic sugar based on archetypal inheritance. Having the characteristics of a prototype object, you can share your properties with new objects.

But there are some differences

1. On writing

In ES5, we implement classes and inheritance through Prototype and Object.create()

function Person(){}
function Student(){}
Student.prototype = new Person()
Copy the code

In ES6, we use the class syntax to implement classes and inheritance through extends, but the actual principle is still implemented through a chain of prototypes

class Person{}
class Student extends Person{}
Copy the code

2. Differences in ascension

In ES5, functions can be promoted, while classes, like let and const, have block-level scope. There is no variable promotion, which can cause errors

3. Strict mode is applied internally

Function in ES5 uses non-strict mode by default, while class in ES6 uses strict mode, so the following is incorrect:

class Foo{
    constructor(){
        fo = 42
    }
}
var foo = new Foo() 
// Uncaught ReferenceError: fo is not defined
Copy the code

4. The class method cannot be enumerated

Methods in ES5 functions can be enumerated, but methods in ES6 classes cannot.

class Foo{
    constructor(){
        this.foo = 42
    }
    fn(){}
    static sfn(){}
}
Object.keys(Foo)
// []
Object.keys(Foo.prototype)
// []
Copy the code

5. Class methods have no prototype objects and cannot be called with new

In ES5, functions built on functions and their prototype objects have prototype objects, so we can construct an object from new, but methods in class cannot construct an object from new

class Foo{
    constructor(){
        this.foo = 42
    }
    static sFn(){}
    fn(){}
}
var foo = new Foo()
var fn = new foo.fn()
// Uncaught TypeError: foo.fn is not a constructor
var sFn = new foo.sFn()
// Uncaught TypeError: foo.sFn is not a constructor
Copy the code

Class must be called using new

In ES5, we used function to simulate a class, but in reality, function is still a function, so we can still call a function directly, whereas with class, we can’t call it directly because it’s not a method

7. The class name cannot be overridden inside the class

For function, as long as the current function scope does not have the same variable name, we can directly refer to the current function variable name inside the function, but for class, this is not possible

class Foo{
    constructor(){
        this.foo = 42
        Foo = 'new Foo'
    }
}
var f = new Foo()
// Uncaught TypeError: Assignment to constant variable.
Copy the code

8. The __proto__ direction is different when implementing inheritance

When we use function in ES5, we do this by changing the prototype reference. When we use class in ES6, __proto__ refers to the parent class

class Fa{}
class Ch extends Fa{}
Ch.__proto__===Fa
// true
Copy the code