ECMAScript 2015 中引入的 JavaScript 类实质上是 JavaScript 现有的基于原型的继承的语法糖。类语法不会为JavaScript引入新的面向对象的继承模型。

定义类

定义类有两种方式,一个是通过 类声明 的方式,另外一种是通过 类表达式 来定义。

类声明

类似于函数声明一样,我们可以通过带有 class 的关键字的类名来定义。

  1. class Rectangle{
  2. constructor(height,width){
  3. this.height = height;
  4. this.weight = width
  5. }
  6. }

提升

函数声明类声明 中有一个重要的区别是,函数声明可以提升,而类声明不行(同样地,在函数表达式中和类表达式中同样不行)。
因此,类似这样的代码将会抛出异常。

  1. let p = new Rectangle(10,10);
  2. // ReferenceError
  3. class Rectangle{
  4. constructor(height,width){
  5. this.height = height;
  6. this.width = width;
  7. }
  8. }

类表达式

一个类表达式是定义一个类的另一种方式。

  1. // 匿名
  2. let Rectangle = class {
  3. constructor(height, width){
  4. this.height = height;
  5. this.width = width;
  6. }
  7. }
  8. // 被命名
  9. let Rectangle = class Rectangle{
  10. constructor(height, width){
  11. this.height = height;
  12. this.width = width;
  13. }
  14. }

类体和方法定义

原型方法

  1. class R{
  2. constructor(width,height){
  3. this.width = width;
  4. this.height = height;
  5. }
  6. // getter
  7. get area(){
  8. return this.calcArea()
  9. }
  10. calcArea(){
  11. return this.width*this.height
  12. }
  13. }
  14. let a = new R(10,10);
  15. console.log(a.area)
  16. // 100

这里需要注意几点。

  1. 在类定义中,构造函数、方法等其他语句块中,不用像对象里用 “,” 分割开来,其次,在 getter 中的函数不用加括号()。

静态方法

static 关键字用来定义一个类的一个静态方法。调用静态方法不需要 实例化 这个类, 且不能通过类的实例对象来调用这个静态方法

  1. class Point {
  2. constructor(x, y) {
  3. this.x = x;
  4. this.y = y;
  5. }
  6. static distance(a, b) {
  7. const dx = a.x - b.x;
  8. const dy = a.y - b.y;
  9. return Math.hypot(dx, dy);
  10. }
  11. }
  12. const p1 = new Point(5, 5);
  13. const p2 = new Point(10, 10);
  14. console.log(Point.distance(p1, p2));

字段声明

字段声明分为共有字段和私有字段

公有字段

  1. class Rectangle{
  2. width = 10;
  3. height;
  4. constructor(width,height){
  5. this.width = width;
  6. this.height = height;
  7. }
  8. }

私有字段

  1. class Rectangle{
  2. #width = 10;
  3. #height;
  4. constructor(width,height){
  5. this.#width = width;
  6. this.#height = height;
  7. }
  8. }

从类外部引用私有字段将是错误的,它们只能在类里读取或写入。

子类

我们使用 extends 来创建子类

  1. class Animal{
  2. constructor(name){
  3. this.name = name
  4. }
  5. speak(){
  6. console.log(this.name + "makes a noise");
  7. }
  8. }
  9. class Dog extends Animal{
  10. // 可以没有这中间的部分,那么 Dog 类将于 Animal 类完全拥有相同的属性与方法
  11. speak(){
  12. console.log(this.name + "barks" )
  13. }
  14. }
  15. let d = new Dog("Xirui");
  16. d.speak(); // Xirui barks

使用 super 调用超类

  1. class Cat {
  2. constructor(name) {
  3. this.name = name;
  4. }
  5. speak() {
  6. console.log(this.name + ' makes a noise.');
  7. }
  8. }
  9. class Lion extends Cat {
  10. speak() {
  11. super.speak(); // 调用类父类的方法
  12. console.log(this.name + ' roars.');
  13. }
  14. }