ES6 中的 class 是什么?和函式构造函式差别是什么?

2023年2月9日

💎 加入 E+ 成長計畫 與超過 300+ 位軟體工程師一同在社群中成長,並且獲得更多的軟體工程學習資源

ES6 class

JavaScript 在 ECMAScript 6 (ES6) 之前并没有 class 的语法,而是会透过函式构造函式建立物件,并再通过 new 关键字实例。

在 ES6 时引入了 class 的概念,JavaScript class 使用的语法 class 似于其他 OOP 程式语言中的 class,但 JavaScript 的 class 是一种语法糖,本质上与其他程式语言 class 的实践方式不一样,JavaScript 的 class 是透过原型继承来模拟 class 的行为。以下方代码为例:

class Car {
  constructor(brand, model) {
    this.brand = brand;
    this.model = model;
  }

  drive() {
    console.log(`Driving a ${this.brand} ${this.model}`);
  }
}

const myCar = new Car("Tesla", "Model 3");
myCar.drive(); // Driving a Tesla Model 3

上方代码是 ES6 的写法,但在 ES6 之前,我们会透过函式实践相同功能

function Car(brand, model) {
  this.brand = brand;
  this.model = model;
}

Car.prototype.drive = function () {
  console.log(`Driving a ${this.brand} ${this.model}`);
};

const myCar = new Car("Tesla", "Model 3");
myCar.drive(); // Driving a Tesla Model 3

可以看到在 ES6 版本中的实践方法,Car class 中的 drive 方法并不是在 class 内部里封装的方法,背后其实是赋值到了 Car 的原型 (prototype) 上而已。

ES6 class 和 ES5 函式构造函式 (constructor function) 的差别

ES6 class 和 ES5 函式构造函式 (constructor function) 主要有几个差别:

  1. 提升 hosting :不同于函式宣告的构造函式存在提升,但使用 class 宣告则是无法再宣告前就使用。

    var newClass = new MyClass(); // Uncaught ReferenceError: MyClass is not defined is not defined
    
    class MyClass {}
    
  2. **new:函式创建的构造函式如果没有透过 new 实例化,就只是执行一般函式。但 class 构造函式则一定需要透过 new 来创建实例,否则会报错。 **

    class Animal {}
    const a = Animal(); // Uncaught TypeError: Class constructor Animal cannot be invoked without 'new'
    

class 的常见方法

继承

ES6 的 class 继承是通过使用 extends 关键字实现的。

假设有一个父 class Animal

class Animal {
  constructor(name) {
    this.name = name;
  }

  eat() {
    console.log(`${this.name} eat food.`);
  }

  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

你可以创建一个子 class Dog,继承 Animal class:

class Dog extends Animal {
  constructor(name) {
    super(name);
  }

  speak() {
    console.log(`${this.name} barks.`);
  }
}

在这个例子中,Dog class 继承了 Animal class 的所有属性和方法,也可以重写 speak 方法。

接着,使用 Dog class 创建物件:

const dog = new Dog("HAHA");
dog.eat(); // HAHA eat food.
dog.speak(); // HAHA barks.

static 静态方法

静态方法不能被物件实例继承,只能通过 class 本身调用。这意味着,你不能通过物件实例调用 class 方法,而只能通过 class 名本身调用。

如果尝试通过对象实例访问静态方法,将抛出一个错误,因为静态方法不能被继承,只能通过 class 本身访问。

class MathHelper {
  static add(a, b) {
    return a + b;
  }
}

const math = new MathHelper();
// 尝试通过对象实例访问静态方法,将抛出一个错误
console.log(math.add(2, 3)); // Uncaught TypeError: math.add is not a function

// 只能通过 class 本身访问
console.log(MathHelper.add(2, 3)); // 5

Private fields

可以通过使用前缀 # 来实现 class 的私有领域 (Private fields),包括建立私有的属性或是方法,而私有领域只能在 class 内部使用,外部无法存取。

如以下代码,#privateField 为私有变数,只能在 Example class 中使用,当实例 example 尝试直接获取 privateField 变数时,会报 SyntaxError 的错误。

class Example {
  #privateField = 100;

  getPrivateField() {
    return this.#privateField;
  }
}

const example = new Example();
console.log(example.getPrivateField()); // 100
console.log(example.#privateField); // SyntaxError: Private field '#privateField' must be declared in an enclosing class
🧵 如果你想收到最即時的內容更新,可以在 FacebookInstagram 上追蹤我們