JS本身是一种基于原型的、动态类型的脚本语言,它不像传统的面向对象编程语言(例如java、c++等)那样拥有类的概念,但是,ECMAScript 6 中引入了类的语法,使得在JavaScript中使用类变得更加直观和方便。下面是对 JavaScript 中类的详细介绍:
一、类的定义
在 ES6 中,可以使用 class 关键字来定义一个类。类的定义包括类名、构造函数 (constructor)和方法。
// 直接定义
class Person {
// 构造函数
constructor(name, age) {
this.name = name;
this.age = age;
}
// 方法
sayHello() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
//表达式定义
const PersonClass = class {
constructor(name) {
this.name = name;
}
sayHello() {
console.log(`Hello, ${this.name}`);
}
};
const person2 = new PersonClass('Charlie');
person2.sayHello(); // 输出: Hello, Charlie
二、创建实例
定义一个类后,我们可以通过 new 关键字来创建类的实例,俗称:实例化。
const person1 = new Person('Alice', 30);
person1.sayHello(); // 输出: Hello, my name is Alice and I am 30 years old.
三、Getter 和 Setter
类中可以定义 getter 和 setter 方法来控制对属性的访问和修改。
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
get area() {
return this.width * this.height;
}
set area(value) {
this.width = value[0];
this.height = value[1];
}
}
const rect = new Rectangle(10, 5);
console.log(rect.area); // 输出: 50
rect.area = [2, 3];
console.log(rect.area); // 输出: 6
四、类的继承与多态
在 JavaScript 中,继承和多态是面向对象编程(OOP)的两个重要概念。ES6 引入的类语法使得这些概念在 JavaScript 中的实现变得更加直观和易于理解。
1、继承
继承允许一个类(子类)继承另一个类(父类)的属性和方法。子类可以重用父类的代码,并可以添加新的属性或重写父类的方法。
1.1、extends关键字
JS中使用extends关键字,来实现类的继承。
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound.`);
}
}
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 类,并重写了 speak 方法。super(name) 用于调用父类的构造函数,以初始化从父类继承的属性。
1.2、super方法
super主要用于访问父类中的属性和方法。
class Cat extends Animal {
constructor(name, color) {
super(name);
this.color = color;
}
speak() {
super.speak(); // 调用父类的 speak 方法
console.log(`${this.name} is a ${this.color} cat.`);
}
}
const cat = new Cat('Whiskers', 'black');
cat.speak();
// 输出:
// Whiskers makes a sound.
// Whiskers is a black cat.
在这个例子中,Cat 类的 speak 方法首先调用了父类 Animal 的 speak 方法,然后添加了额外的输出。
2、多态
多态允许使用统一的接口来调用不同实现的方法。在 JavaScript 中,多态通常通过方法重写和接口模拟来实现。
2.1、方法重写
方法重写是子类提供父类方法的具体实现。在上面的 Dog 和 Cat 类中,我们已经看到了方法重写的例子。每个子类都重写了 speak 方法,以提供适合其特定类型的实现。
2.2、接口模拟
虽然 JavaScript 没有正式的接口概念,但可以通过约定俗成的方法集合来模拟接口。任何实现了这些方法的类都可以被认为是实现了该接口。
class MakeSound {
// 这不是一个真正的接口,只是一个约定
speak() {
throw new Error('*********************');
}
}
class Bird extends MakeSound {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} chirps.`);
}
}
const bird = new Bird('Tweety');
bird.speak(); // 输出: Tweety chirps.
在这个例子中,MakeSound 类可以被视为一个“接口”,因为它定义了一个 speak 方法,但没有提供具体实现。Bird 类实现了这个“接口”,提供了 speak 方法的具体实现。
3、小结
- 继承:通过
extends
关键字,子类可以继承父类的属性和方法,并可以重写父类的方法以提供特定实现。 - 多态:通过方法重写和接口模拟,可以使用统一的接口来调用不同实现的方法,从而实现多态性。
五、总结
ES6 引入的类语法使得 JavaScript 的面向对象编程更加直观和易于理解。尽管 JavaScript 的底层仍然是基于原型的继承机制,但类的语法提供了一个更简洁的抽象层,使得开发者可以更方便地定义和使用类。
注意点:类的声明不会被提升,必须在引用类之前声明它,否则会报错。
// let Person; // 如果尝试提前声明,然后实例化,会报错
class Person {
constructor(name) {
this.name = name;
}
}
const person3 = new Person('Dave'); // 正确