Record es6,class

Posted by edup_pt on Tue, 09 Jun 2020 08:40:08 +0200

The basic syntax summary of ES6 | class
Class and module, the default is strict mode, so you do not need to use strict to specify the running mode. As long as your code is written in classes or modules, only strict patterns are available.

Considering that all code in the future is actually running in modules, ES6 actually upgrades the whole language to strict mode.

1, The basic syntax of Class

1.1 basic grammar

In JavaScript language, the traditional way to generate instance objects is through constructors. Here is an example.

1 function Point(x, y) {
2   this.x = x;
3   this.y = y;
4 }
5 Point.prototype.toString = function () {
6   return '(' + this.x + ', ' + this.y + ')';
7 };
8 
9 var p = new Point(1, 2);

The above writing method is quite different from the traditional object-oriented languages (such as C + + and Java), which can easily confuse the new programmers learning the language.

ES6 provides a more traditional way of writing, and introduces the concept of class as a template for objects. With the class keyword, you can define a class.

Basically, the class of ES6 can be regarded as a grammar sugar. Most of its functions can be achieved by ES5. The new class writing method only makes the writing method of object prototype clearer and more like the syntax of object-oriented programming. The above code is rewritten with the class of ES6, as follows.

 1 //Defining classes
 2 class Point {
 3   constructor(x, y) {
 4     this.x = x;
 5     this.y = y;
 6   }
 7   toString() {
 8     return '(' + this.x + ', ' + this.y + ')';
 9   }
10 }

The above code defines a "class". You can see that there is a constructor method in it, which is the constructor method, and this keyword represents the instance object. That is to say, the Point constructor of ES5 corresponds to the construction method of the Point class of ES6.

The Point class defines a toString method as well as a constructor. Note that when defining "class" methods, you don't need to add the function keyword in front of you, just put the function definition in. In addition, there is no need to separate methods with commas, and an error will be reported if it is added. The class of ES6 can be regarded as another way to write a constructor.

1 class Point {
2   // ...
3 }
4 
5 typeof Point // "function"
6 Point === Point.prototype.constructor // true

The above code shows that the data type of a class is a function, and the class itself points to a constructor.

When it is used, the new command is also used directly for the class, which is exactly the same as the constructor.

1 class Bar {
2   doStuff() {
3     console.log('stuff');
4   }
5 }
6 
7 var b = new Bar();
8 b.doStuff() // "stuff"

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

 1 class Point {
 2   constructor() {
 3     // ...
 4   }
 5   toString() {
 6     // ...
 7   }
 8   toValue() {
 9     // ...
10   }
11 }
12 
13 // Equivalent to
14 Point.prototype = {
15   constructor() {},
16   toString() {},
17   toValue() {},
18 };

To call a method on an instance of a class is to call a method on the prototype.

class B {}
let b = new B();

b.constructor === B.prototype.constructor // true

In the above code, B is an instance of class B, and its constructor method is the constructor method of class B prototype.

Because the methods of the class are all defined on the prototype object, the new methods of the class can be added on the prototype object. Object.assign Method can easily add more than one method to a class at a time.

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

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

The constructor property of the prototype object points directly to the "class" itself, which is consistent with the behavior of ES5.

Point.prototype.constructor === Point // true

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

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

  toString() {
    // ...
  }
}

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

In the above code, toString method is a method defined inside the Point class, which is not enumerable. This is not consistent with the behavior of ES5.

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

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

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

The above code is written in ES5, and toString is enumerable.

The property name of the class, which can be an expression.

 1 let methodName = 'getArea';
 2 
 3 class Square {
 4   constructor(length) {
 5     // ...
 6   }
 7 
 8   [methodName]() {
 9     // ...
10   }
11 }

In the above code, the method name getArea of the Square class is obtained from the expression.

1.2 constructor method

Constructor method is the default method of a class, which is called automatically when an object instance is generated through the new command. A class must have a constructor method. If it is not explicitly defined, an empty constructor method will be added by default.

class Point {
}

// Equivalent to
class Point {
  constructor() {}
}

In the above code, an empty class Point is defined, and the JavaScript engine will automatically add an empty constructor method for it.

The constructor method returns the instance object (that is, this) by default. You can specify to return another object.

1 class Foo {
2   constructor() {
3     return Object.create(null);
4   }
5 }
6 
7 new Foo() instanceof Foo
8 // false

In the above code, the constructor function returns a new object, resulting in the instance object not being an instance of the Foo class.

Class must be called with new, otherwise an error will be reported. This is a major difference between it and ordinary constructors, which can be executed without new.

1 class Foo {
2   constructor() {
3     return Object.create(null);
4   }
5 }
6 
7 Foo()
8 // TypeError: Class constructor Foo cannot be invoked without 'new'

Instance object of class 1.3

The writing method of the instance object of the generated Class is exactly the same as ES5, and the new command is also used. As mentioned before, if you forget to add new and call Class like a function, an error will be reported.

class Point {
  // ...
}

// report errors
var point = Point(2, 3);

// correct
var point = new Point(2, 3);

Like ES5, an instance's properties are defined on the prototype (that is, class) unless they are explicitly defined on itself (that is, on this object).

In the above code, x and y are the properties of the instance object point itself (defined on this variable), so the hasOwnProperty method returns true, while toString is the property of the prototype object (defined on the point class), so the hasOwnProperty method returns false. These are consistent with the behavior of ES5.

Like ES5, all instances of a class share a prototype object.

var p1 = new Point(2,3);
var p2 = new Point(3,2);

p1.__proto__ === p2.__proto__

//true
In the above code, p1 and p2 are instances of point, and their prototypes are Point.prototype , so__ proto__ Properties are equal.

This also means that the__ proto__ Property to add a method to the class.

proto is not a feature of the language itself. It is a private attribute added by major manufacturers when they implement it. Although many modern browser JS engines currently provide this private attribute, it is still not recommended to use this attribute in production to avoid dependence on the environment. In the production environment, we can use Object.getPrototypeOf Method to get the prototype of the instance object, and then add the method / property to the prototype.

 1 var p1 = new Point(2,3);
 2 var p2 = new Point(3,2);
 3 
 4 p1.__proto__.printName = function () { return 'Oops' };
 5 
 6 p1.printName() // "Oops"
 7 p2.printName() // "Oops"
 8 
 9 var p3 = new Point(4,2);
10 p3.printName() // "Oops"

The above code adds a printName method to the prototype of p1. Since the prototype of p1 is the prototype of p2, p2 can also call this method. Moreover, the new instance p3 can call this method. This means that the__ proto__ Property rewriting prototype must be very careful and not recommended, because it will change the original definition of "class" and affect all instances.

1.4 Class expression

Like functions, classes can be defined in the form of expressions.

1 const MyClass = class Me {
2   getClassName() {
3     return Me.name;
4   }
5 };

The above code defines a Class using an expression. It should be noted that the name of this Class is MyClass instead of me. Me is only available in the internal code of Class and refers to the current Class.

1 let inst = new MyClass();
2 inst.getClassName() // Me
3 Me.name // ReferenceError: Me is not defined

The above code indicates that Me is only defined within Class.

If the inner part of the class is not used, you can omit Me, that is, you can write it in the following form.

const MyClass = class { /* ... */ };
With the Class expression, you can write out the immediately executed Class. In the code, person is an instance of an immediate execution Class.

 1 let person = new class {
 2   constructor(name) {
 3     this.name = name;
 4   }
 5 
 6   sayName() {
 7     console.log(this.name);
 8   }
 9 }('Zhang San');
10 
11 person.sayName(); // "Zhang San"

1.5 no variable promotion

Class has no hoist, which is quite different from ES5.

new Foo(); // ReferenceError
class Foo {}

In the above code, Foo class is used before and defined after, so an error will be reported, because ES6 will not promote the class declaration to the code header. The reason for this rule is related to the inheritance mentioned below, and it is necessary to ensure that the subclass is defined after the parent class.

1 {
2   let Foo = class {};
3   class Bar extends Foo {
4   }
5 }

The above code will not report an error, because when Bar inherits Foo, Foo has already been defined. However, if there is class promotion, the above code will report an error, because class will be promoted to the code header, and let command is not promoted, so when Bar inherits Foo, Foo has not been defined.

1.6 private methods and private properties

Private method:

Private method is a common requirement, but ES6 does not provide it, so it can only be implemented through flexible method simulation.

One approach is to make a distinction in naming:

class Widget {
  // public Method 
  foo (baz) {
    this._bar(baz);
  }

  // Private method
  _bar(baz) {
    return this.snaf = baz;
  }
  // ...
}

In the above code_ The underscore in front of the bar method indicates that it is a private method for internal use only. However, this kind of naming is not guaranteed. Outside the class, you can still call this method.

Another way is to simply remove private methods from the module, because all methods inside the module are visible to the outside.

class Widget {
  foo (baz) {
    bar.call(this, baz);
  }

  // ...
}

function bar(baz) {
  return this.snaf = baz;
}

In the above code, foo is a public method, which is called internally bar.call(this, baz). This makes bar actually the private method of the current module.

Another way is to use the uniqueness of Symbol value to name the private method as a Symbol value.

 1 const bar = Symbol('bar');
 2 const snaf = Symbol('snaf');
 3 
 4 export default class myClass{
 5 
 6   // public Method 
 7   foo(baz) {
 8     this[bar](baz);
 9   }
10 
11   // Private method
12   [bar](baz) {
13     return this[snaf] = baz;
14   }
15 
16   // ...
17 };

In the above code, bar and snaf are Symbol values, which make them unavailable to the third party, thus achieving the effect of private methods and private properties.

1.7 PS: proposal of private property

Like private methods, ES6 does not support private properties. At present, there is a proposal to add private attribute to class. The method is to use ා before the property name.

 1 class Point {
 2   #x;
 3 
 4   constructor(x = 0) {
 5     #x = +x; // It can be written as this
 6   }
 7 
 8   get x() { return #x }
 9   set x(value) { #x = +value }
10 }

In the above code, ාx represents the private property x, which cannot be read outside the Point class. You can also see that private properties and instance properties can have the same name (for example, ාx and get x()).

A private property can specify an initial value, which is initialized when the constructor executes.

class Point {
  #x = 0;
  constructor() {
    #x; // 0
  }
}

The reason why we want to introduce a new prefix ා to represent the private attribute instead of using the private keyword is that JavaScript is a dynamic language, using independent symbols seems to be the only reliable way to accurately distinguish whether an attribute is a private attribute. In addition, Ruby language uses @ to represent private properties, and ES6 does not use this symbol because @ has been left to Decorator.

The proposal only specifies the writing of private property. But, naturally, it can also be used to write private methods.

1 class Foo {
2   #a;
3   #b;
4   #sum() { return #a + #b; }
5   printSum() { console.log(#sum()); }
6   constructor(a, b) { #a = a; #b = b; }
7 }

In the above code, ාsum() is a private method.

In addition, private properties can also set getter and setter methods.

class Counter {
  #xValue = 0;

  get #x() { return #xValue; }
  set #x(value) {
    this.#xValue = value;
  }

  constructor() {
    super();
    // ...
  }
}

In the above code, ාx is a private property, and its reading and writing are completed through getාx() and setාx().

1.8 direction of this

If there is this inside the method of a class, it points to the instance of the class by default. However, great care must be taken to report errors if the method is used alone.

 1 class Logger {
 2   printName(name = 'there') {
 3     this.print(`Hello ${name}`);
 4   }
 5 
 6   print(text) {
 7     console.log(text);
 8   }
 9 }
10 
11 const logger = new Logger();
12 const { printName } = logger;
13 printName(); // TypeError: Cannot read property 'print' of undefined

In the above code, this in the printName method points to the instance of the Logger class by default. However, if this method is extracted and used alone, this will point to the environment in which the method runs, and an error will be reported because the print method cannot be found.

A simpler solution is to bind this in the constructor so that the print method cannot be found.

1 class Logger {
2   constructor() {
3     this.printName = this.printName.bind(this);
4   }
5   // ...
6 }

Another solution is to use the arrow function

class Logger {
  constructor() {
    this.printName = (name = 'there') => {
      this.print(`Hello ${name}`);
    };
  }

  // ...
}

Another solution is to use proxy. When getting methods, bind this / / [todo] – proxy automatically?

View Code
1.9 name attribute

Because ES6's Class is essentially a wrapper around ES5's constructor, many features of the function are inherited by Class, including the name attribute.

1 class Point {}
2 Point.name // "Point"

The name attribute always returns the class name immediately following the class keyword.

  1. getter and setter of 10 class

Like ES5, you can use the get and set keywords in the "class" to set the store value function and value function for a property, and block the access behavior of the property.

class MyClass {
  constructor() {
    // ...
  }
  get prop() {
    return 'getter';
  }
  set prop(value) {
    console.log('setter: '+value);
  }
}

let inst = new MyClass();
inst.prop = 123;
// setter: 123
inst.prop
// 'getter'

In the above code, the prop attribute has the corresponding store value function and value taking function, so the assignment and reading behaviors are customized.

The store value function and the value taking function are set on the Descriptor object of the property.

View Code
In the above code, the stored value function and the value taking function are defined on the description object of the html attribute, which is completely consistent with ES5.

generator method of 1.11 class

If a method is preceded by an asterisk (*), it is a Generator function.

class Foo {
  constructor(...args) {
    this.args = args;
  }
  * [Symbol.iterator]() {
    for (let arg of this.args) {
      yield arg;
    }
  }
}

for (let x of new Foo('hello', 'world')) {
  console.log(x);
}
// hello
// world

In the above code, Foo Symbol.iterator Method is preceded by an asterisk indicating that the method is a Generator function. Symbol.iterator Method returns a default iterator of Foo class, for The of loop automatically calls the traverser.

  1. 12 class static method

Class is the prototype of an instance. All methods defined in the class will be inherited by the instance. If the static keyword is added before a method, it means that the method will not be inherited by the instance, but called directly through the class, which is called "static method".

 1 class Foo {
 2   static classMethod() {
 3     return 'hello';
 4   }
 5 }
 6 
 7 Foo.classMethod() // 'hello'
 8 
 9 var foo = new Foo();
10 foo.classMethod()
11 // TypeError: foo.classMethod is not a function

In the above code, there is a static keyword in front of the classmethod method method of the foo class, indicating that the method is a static method and can be called directly on the foo class( Foo.classMethod()) instead of being called on an instance of the foo class. If you call a static method on an instance, an error is thrown indicating that the method does not exist.

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

 1 class Foo {
 2   static bar () {
 3     this.baz();
 4   }
 5   static baz () {
 6     console.log('hello');
 7   }
 8   baz () {
 9     console.log('world');
10   }
11 }
12 
13 Foo.bar() // hello

In the above code, the static method bar calls this.baz , this refers to the foo class instead of an instance of foo, which is equivalent to calling Foo.baz . In addition, you can see from this example that static methods can have the same name as non static methods.

The static method of the parent class can be inherited by the child class.

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
}

Bar.classMethod() // 'hello'

In the above code, the parent Foo has a static method, which can be called by the child Bar.

Static methods can also be called from super objects.

Static methods can also be called from super

  1. Static and example properties of 13 class

Static attribute refers to the attribute of class itself, i.e Class.propName , instead of defining properties on the instance object (this).

class Foo {
}

Foo.prop = 1;
Foo.prop // 1

The above method defines a static property prop for Foo class.

At present, only this writing method is feasible, because ES6 explicitly stipulates that there are only static methods in Class and no static attributes.

// Neither of the following is valid
class Foo {
  // Style 1
  prop: 2

  // Writing method 2
  static prop: 2
}

Foo.prop // undefined

At present, there is a proposal for static attributes, which prescribes new writing methods for instance attributes and static attributes.

(1) Instance properties of class

The instance properties of a class can be written into the definition of the class by using the equation.

1 class MyClass {
2   myProp = 42;
3 
4   constructor() {
5     console.log(this.myProp); // 42
6   }
7 }

In the above code, myProp is the instance property of MyClass. On the instance of MyClass, you can read this property.

Previously, we defined instance properties, which can only be written in the constructor method of a class.

class ReactCounter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }
}
``
//In the above code, the constructor definesthis.state Property.

//With the new writing method, it can not be defined in the constructor method.
```javascript
class ReactCounter extends React.Component {
  state = {
    count: 0
  };
}

This style of writing is clearer than before.

For readability purposes, the new writing method allows direct listing of instance properties that have already been defined in the constructor.

class ReactCounter extends React.Component {
  state;
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }
}

(2) Static properties of class

As long as the static attribute of the class is written in front of the instance attribute above and the static keyword is added.

class MyClass {
  static myStaticProp = 42;

  constructor() {
    console.log(MyClass.myStaticProp); // 42
  }
}

Similarly, this new method greatly facilitates the expression of static attributes.

// Old style
class Foo {
  // ...
}
Foo.prop = 1;

// neographism
class Foo {
  static prop = 1;
}

In the above code, the static properties of the old method are defined outside the class. After the whole class is generated, static attributes are generated. This makes it easy to ignore this static property and does not conform to the code organization principle that relevant code should be put together. In addition, the new writing method is explicit declaration rather than assignment processing, so the semantics is better.

  1. 14 new target

New is the command to generate the instance object from the constructor. ES6 introduces a new command new.target Property, which is commonly used in constructors, returns the constructor to which the new command acts. If the constructor is not called through the new command, new.target undefined is returned, so this property can be used to determine how the constructor is called.

 1 function Person(name) {
 2   if (new.target !== undefined) {
 3     this.name = name;
 4   } else {
 5     throw new Error('Must use new Command build instance');
 6   }
 7 }
 8 
 9 // Another way of writing
10 function Person(name) {
11   if (new.target === Person) {
12     this.name = name;
13   } else {
14     throw new Error('Must use new Command build instance');
15   }
16 }
17 
18 var person = new Person('Zhang San'); // correct
19 var notAPerson = Person.call(person, 'Zhang San');  // report errors

The above code ensures that the constructor can only be called through the new command.

Class internal call new.target , returns the current class.

class Rectangle {
  constructor(length, width) {
    console.log(new.target === Rectangle);
    this.length = length;
    this.width = width;
  }
}

var obj = new Rectangle(3, 4); // Output true

Note that when a subclass inherits a parent class, new.target Will return the subclass.

class Rectangle {
  constructor(length, width) {
    console.log(new.target === Rectangle);
    // ...
  }
}

class Square extends Rectangle {
  constructor(length) {
    super(length, length);
  }
}

var obj = new Square(3); // Output false

In the above code, new.target Will return the subclass.

With this feature, you can write classes that cannot be used independently and can only be used after inheritance.

class Shape {
  constructor() {
    if (new.target === Shape) {
      throw new Error('This class cannot be instantiated');
    }
  }
}

class Rectangle extends Shape {
  constructor(length, width) {
    super();
    // ...
  }
}

var x = new Shape();  // report errors
var y = new Rectangle(3, 4);  // correct

In the above code, the Shape class cannot be instantiated and can only be used for inheritance.

Note that outside the function, use the new.target It will report an error.

Topics: Attribute Javascript React Asterisk