类是用于创建对象的模板。他们用代码封装数据以处理该数据。 JS中的类建立在原型上,但也具有某些语法和语义未与ES5类相似语义共享。
定义
实际上,类是“特殊的函数”,就像你能够定义的函数表达式和函数声明一样,类语法有两个组成部分:类表达式和类声明。
类声明
定义类的一种方法是使用类声明。要声明一个类,你可以使用带有class关键字的类名(这里是“Rectangle”)。
class Rectangle {constructor(height, width) {this.height = height;this.width = width;}}
提升
函数声明和类声明之间的一个重要区别在于, 函数声明会提升,类声明不会。你首先需要声明你的类,然后再访问它,否则类似以下的代码将抛出ReferenceError:
let p = new Rectangle(); // ReferenceErrorclass Rectangle {}
类表达式
类表达式是定义类的另一种方法。类表达式可以命名或不命名。命名类表达式的名称是该类体的局部名称。(不过,可以通过类的(而不是一个实例的) name 属性来检索它)。
// 未命名/匿名类let Rectangle = class {constructor(height, width) {this.height = height;this.width = width;}};console.log(Rectangle.name);// output: "Rectangle"// 命名类let Rectangle = class Rectangle2 {constructor(height, width) {this.height = height;this.width = width;}};console.log(Rectangle.name);// 输出: "Rectangle2"
类体和方法定义
一个类的类体是一对花括号/大括号 {} 中的部分。这是你定义类成员的位置,如方法或构造函数。
严格模式
类声明和类表达式的主体都执行在严格模式下。比如,构造函数,静态方法,原型方法,getter和setter都在严格模式下执行。
构造函数
constructor方法是一个特殊的方法,这种方法用于创建和初始化一个由class创建的对象。一个类只能拥有一个名为 “constructor”的特殊方法。如果类包含多个constructor的方法,则将抛出 一个SyntaxError 。
一个构造函数可以使用 super 关键字来调用一个父类的构造函数。
原型方法
参见方法定义。
class Rectangle {// constructorconstructor(height, width) {this.height = height;this.width = width;}// Getterget area() {return this.calcArea()}// MethodcalcArea() {return this.height * this.width;}}const square = new Rectangle(10, 10);console.log(square.area);
静态方法
[static](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes/static) 关键字用来定义一个类的一个静态方法。调用静态方法不需要实例化)该类,但不能通过一个类实例调用静态方法。静态方法通常用于为一个应用程序创建工具函数。
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);p1.displayName;// undefinedp1.distance;// undefinedconsole.log(Point.displayName);// "Point"console.log(Point.distance(p1, p2));// 7.0710678118654755
用原型和静态方法绑定 this
当调用静态或原型方法时没有指定 this 的值,那么方法内的 this 值将被置为 undefined。即使你未设置 “use strict” ,因为 class 体内部的代码总是在严格模式下执行。
class Animal {speak() {return this;}static eat() {return this;}}let obj = new Animal();obj.speak(); // Animal {}let speak = obj.speak;speak(); // undefinedAnimal.eat() // class Animallet eat = Animal.eat;eat(); // undefined
如果上述代码通过传统的基于函数的语法来实现,那么依据初始的 this 值,在非严格模式下方法调用会发生自动装箱。若初始值是 undefined,this 值会被设为全局对象。
严格模式下不会发生自动装箱,this 值将保留传入状态。
function Animal() { }Animal.prototype.speak = function() {return this;}Animal.eat = function() {return this;}let obj = new Animal();let speak = obj.speak;speak(); // global objectlet eat = Animal.eat;eat(); // global object
实例属性
实例的属性必须定义在类的方法里:
class Rectangle {constructor(height, width) {this.height = height;this.width = width;}}
静态的或原型的数据属性必须定义在类定义的外面。
Rectangle.staticWidth = 20;Rectangle.prototype.prototypeWidth = 25;
字段声明
公共和私有字段声明是JavaScript标准委员会TC39提出的实验性功能(第3阶段)。浏览器中的支持是有限的,但是可以通过Babel等系统构建后使用此功能。
公有字段声明
使用JavaScript字段声明语法,上面的示例可以写成:
class Rectangle {height = 0;width;constructor(height, width) {this.height = height;this.width = width;}}
通过预先声明字段,类定义变得更加自我记录,并且字段始终存在。
正如上面看到的,这个字段可以用也可以不用默认值来声明。
私有字段声明
使用私有字段,可以按以下方式细化定义。
class Rectangle {#height = 0;#width;constructor(height, width) {this.#height = height;this.#width = width;}}
从类外部引用私有字段是错误的。它们只能在类里面中读取或写入。通过定义在类外部不可见的内容,可以确保类的用户不会依赖于内部,因为内部可能在不同版本之间发生变化。
私有字段仅能在字段声明中预先定义。
私有字段不能通过在之后赋值来创建它们,这种方式只适用普通属性。
更多信息,请看class fields.
extends
[extends](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes/extends) 关键字在 类声明 或 类表达式 中用于创建一个类作为另一个类的一个子类。
class Animal {constructor(name) {this.name = name;}speak() {console.log(`${this.name} makes a noise.`);}}class Dog extends Animal {constructor(name) {super(name); // 调用超类构造函数并传入name参数}speak() {console.log(`${this.name} barks.`);}}var d = new Dog('Mitzie');d.speak();// 'Mitzie barks.'
