classes
From: developer.mozilla.org/zh-CN/docs/…
类实际上是个“特殊的函数”,就像你能够定义的函数表达式和函数声明一样,类语法有两个组成部分:类表达式和类声明。
1. 定义一个类的一种方法是使用一个类声明(class declaration)。要声明一个类,你可以使用带有class关键字的类名(如“Rectangle”)。
函数声明和类声明之间的一个重要区别是函数声明会提升(hoisting),类声明不会。你首先需要声明你的类,然后访问它,否则像下面的代码会抛出一个ReferenceError:
let p = new Rectangle();
// ReferenceError
class Rectangle {}
2. 一个类表达式是定义一个类的另一种方式。类表达式可以是被命名的或匿名的。赋予一个命名类表达式的名称是类的主体的本地名称。
// unnamed
let Rectangle = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
console.log(Rectangle.name);
// output: "Rectangle"
// named
let Rectangle = class Rectangle2 {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
console.log(Rectangle.name);
// output: "Rectangle2"
3. static 关键字用来定义一个类的一个静态方法。调用静态方法不需要实例化该类,但不能通过一个类实例调用静态方法。静态方法通常用于为一个应用程序创建工具函数。
The static keyword defines a static method for a class. Static methods are called without instantiating(an example of class instance is "var John = new Person()") their class and cannot be called through a class instance. Static methods are often used to create utility functions for an application.
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
static distance(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.hypot(dx, dy);
}
}
const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
console.log(Point.distance(p1, p2)); // 7.0710678118654755
4. 用原型和静态方法包装
当静态(static)或原型方法被调用时,如果该对象没有“this”值(或“this”作为布尔,字符串,数字,未定义或null) ,那么“this”值在被调用的函数内部将为 undefined,不会发生自动包装。即使我们以非严格模式编写代码,它的行为也是一样的,因为所有的函数、方法、构造函数、getters或setters都在严格模式下执行。因此如果我们没有指定this的值,this值将为undefined。
When a static or prototype method is called without a value for this, the this value will be undefined inside the method. This behavior will be the same even if the "use strict" directive isn't present, because code within the class body's syntactic boundary is always executed in strict mode.
class Animal {
speak() {
return this;
}
static eat() {
return this;
}
}
let obj = new Animal();
obj.speak(); // Animal {}
let speak = obj.speak;
speak(); // undefined
Animal.eat() // class Animal
let eat = Animal.eat;
eat(); // undefined
如果我们使用传统的基于函数的类来编写上述代码,由于这种情况是处于非strict模式,基于调用该函数的“this”值将发生自动装箱。如果初始this值是undefined,将会直接设定到global对象上。
自动装箱不会发生在严格模式里, 此时this的值将保持原样(如上undefined)而被传递。 Autoboxing will not happen in strict mode, the this value remains as passed.
function Animal() { }
Animal.prototype.speak = function() {
return this;
}
Animal.eat = function() {
return this;
}
let obj = new Animal();
let speak = obj.speak;
speak(); // global object
let eat = Animal.eat;
eat(); // global object
5. 设定实例属性与静态类属性以及原型数据属性的区别
Instance properties must be defined inside of class methods:
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
Static class-side properties and prototype data properties must be defined outside of the ClassBody declaration:
Rectangle.staticWidth = 20;
Rectangle.prototype.prototypeWidth = 25;
6. Field declarationsSection
Public and private field declarations are an experimental feature (stage 3) proposed at TC39, the JavaScript standards committee. Support in browsers is limited, but the feature can be used through a build step with systems like Babel.
Public field declarations With the JavaScript field declaration syntax, the above example can be written as:
class Rectangle {
height = 0;
width;
constructor(height, width) {
this.height = height;
this.width = width;
}
}
By declaring fields up-front, class definitions become more self-documenting, and the fields are always present.
As seen above, the fields can be declared with or without a default value.
Private field declarations Using private fields, the definition can be refined as below.
class Rectangle {
#height = 0;
#width;
constructor(height, width) {
this.#height = height;
this.#width = width;
}
}
It's an error to reference private fields from outside of the class; they can only be read or written within the class body. By defining things which are not visible outside of the class, you ensure that your classes' users can't depend on internals, which may change version to version.
Private fields can only be declared up-front in a field declaration.
Private fields cannot be created later through assigning to them, the way that normal properties can.
7. 使用 extends 创建子类节
extends 关键字在类声明或类表达式中用于创建一个类作为另一个类的一个子类。
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
class Dog extends Animal {
speak() {
console.log(this.name + ' barks.');
}
}
var d = new Dog('Mitzie');
d.speak();// 'Mitzie barks.'
如果子类中存在构造函数,则需要在使用this之前首先调用 super()。
也可以扩展传统的基于函数的“类”:
function Animal (name) {
this.name = name;
}
Animal.prototype.speak = function () {
console.log(this.name + ' makes a noise.');
}
class Dog extends Animal {
speak() {
super.speak();
console.log(this.name + ' barks.');
}
}
var d = new Dog('Mitzie');
d.speak();//Mitzie makes a noise. Mitzie barks.
请注意,类不能继承常规(非可构造)对象。如果要继承常规对象,可以改用Object.setPrototypeOf():
var Animal = {
speak() {
console.log(this.name + ' makes a noise.');
}
};
class Dog {
constructor(name) {
this.name = name;
}
}
Object.setPrototypeOf(Dog.prototype, Animal);// If you do not do this you will get a TypeError when you invoke speak
var d = new Dog('Mitzie');
d.speak(); // Mitzie makes a noise.
8. Species
你可能希望在派生数组类 MyArray 中返回 Array对象。这种 species 方式允许你覆盖默认的构造函数。
例如,当使用像map()返回默认构造函数的方法时,您希望这些方法返回一个父Array对象,而不是MyArray对象。Symbol.species 符号可以让你这样做:
class MyArray extends Array {
// Overwrite species to the parent Array constructor
static get [Symbol.species]() { return Array; }
}
var a = new MyArray(1,2,3);
var mapped = a.map(x => x * x);
console.log(mapped instanceof MyArray);
// false
console.log(mapped instanceof Array);
// true
9. Mix-ins
抽象子类或者 mix-ins 是类的模板。 一个 ECMAScript class只能有一个superclass,所以想要从工具类来多重继承的行为是不可能的。子类继承的只能是父类提供的功能性。因此,例如,从工具类的多重继承是不可能的。该功能必须由superclass提供。
一个以superclass作为输入的函数和一个继承该superclass的subclass作为输出可以用于在ECMAScript中实现混合:
var calculatorMixin = Base => class extends Base {
calc() { }
};
var randomizerMixin = Base => class extends Base {
randomize() { }
};
使用 mix-ins 的类可以像下面这样写:
class Foo { }
class Bar extends calculatorMixin(randomizerMixin(Foo)) { }