文章

Class & Super

Class & Super

学习Class 类、Super、getPrototypeOf() 和 setPrototypeOf()

ES6 引入了许多增强 JavaScript 面向对象编程的特性,类(Class)superObject.getPrototypeOf()Object.setPrototypeOf() 是其中的核心概念。它们为对象继承和原型链操作提供了更加简洁、直观的语法,改善了开发者使用 JavaScript 进行面向对象编程的体验。

一、Class 类

在 ES6 之前,JavaScript 使用构造函数和原型来实现对象继承。而 ES6 的 class 语法提供了一个更接近传统面向对象编程语言的定义方式,同时其底层仍然基于 JavaScript 的原型机制。

1. Class 的定义

class 是用来定义对象模板的语法,可以包含构造函数、方法和静态方法。

1
2
3
4
5
6
7
8
9
10
11
12
class Animal {
  constructor(name) {
    this.name = name;
  }

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

const animal = new Animal('Dog');
animal.speak();  // 输出: Dog makes a sound.

在这个例子中,我们定义了一个名为 Animal 的类,并通过 constructor 函数初始化类的属性。speak 是该类的一个方法,所有通过 Animal 类创建的实例都可以调用这个方法。

2. 类的继承

通过 extends 关键字可以实现类的继承。继承使得一个类可以获取另一个类的属性和方法,并可以通过子类进行扩展或重写。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Dog extends Animal {
  constructor(name, breed) {
    super(name);  // 调用父类的构造函数
    this.breed = breed;
  }

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

const dog = new Dog('Buddy', 'Golden Retriever');
dog.speak();  // 输出: Buddy barks.

这里的 Dog 类继承了 Animal 类,通过 super 调用父类的构造函数,继承了 name 属性。我们还重写了 speak 方法,使 Dog 类具有自己独特的行为。

3. 静态方法

使用 static 关键字,可以定义类的静态方法。静态方法属于类本身,而不是类的实例。

1
2
3
4
5
6
7
class Utility {
  static calculateAge(birthYear) {
    return new Date().getFullYear() - birthYear;
  }
}

console.log(Utility.calculateAge(1990));  // 输出: 当前年份 - 1990

在这个例子中,calculateAgeUtility 类的静态方法,可以通过类本身调用,而不需要实例化对象。

二、Super

super 关键字用于调用父类的构造函数或方法。在继承关系中,子类需要调用父类的构造函数以确保父类的属性得以正确初始化。

1. 在构造函数中使用 super

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Animal {
  constructor(name) {
    this.name = name;
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);  // 调用父类构造函数
    this.breed = breed;
  }
}

const dog = new Dog('Buddy', 'Golden Retriever');
console.log(dog.name);  // 输出: Buddy
console.log(dog.breed);  // 输出: Golden Retriever

在这个例子中,Dog 类继承自 Animal 类,super(name) 确保了 name 属性被父类的构造函数初始化。

2. 在方法中使用 super

除了在构造函数中,super 还可以用于调用父类中的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Animal {
  speak() {
    console.log('Animal makes a sound.');
  }
}

class Dog extends Animal {
  speak() {
    super.speak();  // 调用父类的 speak 方法
    console.log('Dog barks.');
  }
}

const dog = new Dog();
dog.speak();
// 输出:
// Animal makes a sound.
// Dog barks.

在这个例子中,super.speak() 用于调用父类的 speak 方法,然后在子类 Dog 中追加自己的行为。

三、getPrototypeOf() 与 setPrototypeOf()

JavaScript 的原型机制是实现对象继承的核心,而 ES6 提供了 Object.getPrototypeOf()Object.setPrototypeOf() 来操作对象的原型。

1. Object.getPrototypeOf()

Object.getPrototypeOf() 方法返回指定对象的原型(即其内部的 [[Prototype]])。

1
2
3
const obj = {};
const proto = Object.getPrototypeOf(obj);
console.log(proto);  // 输出: Object.prototype

这个方法可以让我们明确知道对象的原型是什么,它是对象继承关系中的一个重要概念。

使用场景

  • 检查对象的继承关系:通过 Object.getPrototypeOf(),可以验证某个对象是否继承自另一个对象。

    1
    2
    3
    
    class Animal {}
    const dog = new Animal();
    console.log(Object.getPrototypeOf(dog) === Animal.prototype);  // 输出: true
    

2. Object.setPrototypeOf()

Object.setPrototypeOf() 方法用于设置对象的原型。它允许我们动态地改变对象的继承关系。

1
2
3
4
5
6
7
8
9
const obj = {};
const newProto = {
  greet() {
    console.log('Hello from new prototype!');
  }
};

Object.setPrototypeOf(obj, newProto);
obj.greet();  // 输出: Hello from new prototype!

通过 Object.setPrototypeOf(),我们可以将 obj 的原型动态修改为 newProto,从而使 obj 继承 newProto 的属性和方法。

使用场景

  • 动态修改原型链:在某些复杂场景中,可能需要在运行时动态修改对象的原型链来调整行为。

  • 实现继承链的调整:例如,在某些设计模式中,你可能希望在运行时改变对象的继承路径。

四、实际使用场景

1. 构建对象继承关系

通过 classsuper,可以轻松实现类与类之间的继承关系。无论是用于创建面向对象的系统、游戏开发中的角色继承,还是在构建复杂的用户界面组件时,classsuper 提供了清晰的结构化继承方式。

2. 原型链的动态操作

Object.getPrototypeOf()Object.setPrototypeOf() 可以帮助开发者在运行时调整对象的继承关系。它们对于某些需要动态改变对象行为的场景特别有用,例如插件系统中动态扩展对象功能。

3. 静态方法的应用

静态方法特别适合定义与实例无关的工具函数。例如,常见的数学计算、日期格式化等都可以通过静态方法实现,从而避免创建冗余的对象实例。

五、总结

ES6 的 classsuperObject.getPrototypeOf()Object.setPrototypeOf() 提供了一种更加直观和简洁的方式来处理 JavaScript 中的面向对象编程。类的继承和静态方法的使用极大简化了代码的可读性,而 getPrototypeOfsetPrototypeOf 则为动态原型链操作提供了灵活性。

理解并熟练使用这些特性,能够让我们在构建复杂应用程序时,编写出更加优雅、简洁和高效的代码。

本文由作者按照 CC BY 4.0 进行授权