《现代Javascript高级教程》面向对象编程与Class

矫情吗;* 2023-10-13 11:09 148阅读 0赞





















点击在线阅读,体验更好 链接
现代JavaScript高级小册 链接
深入浅出Dart 链接
现代TypeScript高级小册 链接

面向对象编程与Class

引言

随着JavaScript的发展,ECMAScript 6(ES6)引入了许多新的语言特性和语法糖,其中包括了面向对象编程的Class(类)机制。Class提供了一种更简洁、更直观的方式来定义对象和操作对象的行为。本文将介绍ES6中Class的概念、语法和特性,并通过示例代码来说明其实际应用。

1. 什么是面向对象编程?

面向对象编程(Object-Oriented Programming,简称OOP)是一种编程范式,它将程序中的对象作为基本单元,通过封装、继承和多态等机制来组织和管理代码。面向对象编程将现实世界中的实体抽象为代码中的对象,对象拥有自己的状态(属性)和行为(方法),并与其他对象进行交互。

面向对象编程有以下几个核心概念:

  • 封装(Encapsulation):将数据和操作数据的方法封装在一个对象中,使其成为一个独立的实体,外部无法直接访问对象的内部实现细节。
  • 继承(Inheritance):通过定义一个基类(父类),其他类可以继承该基类的属性和方法,并可以在此基础上进行扩展或覆盖。
  • 多态(Polymorphism):不同对象可以对相同的方法做出不同的响应,即同一个方法可以根据调用对象的不同而具有不同的行为。

面向对象编程的优势包括代码的可重用性、可维护性、扩展性和灵活性等。

2. Class的基本概念

在ES6之前,JavaScript中的对象和面向对象编程的概念相对比较模糊。ES6引入了Class机制,使得JavaScript可以更加直观地定义和使用类。Class是一种特殊的函数,通过Class关键字定义。Class中可以定义构造函数、属性和方法等。

一个简单的Class示例如下:

  1. class Rectangle {
  2. constructor(width, height) {
  3. this.width = width;
  4. this.height = height;
  5. }
  6. area() {
  7. return this.width * this.height;
  8. }
  9. perimeter() {
  10. return 2 * (this.width + this.height);
  11. }
  12. }

在上述示例中,我们定义了一个名为Rectangle的类,

它具有widthheight两个属性,以及area()perimeter()两个方法。通过Class定义的类可以通过实例化来创建具体的对象,并调用其属性和方法。

  1. const rect = new Rectangle(5, 3);
  2. console.log(rect.area()); // 输出:15
  3. console.log(rect.perimeter()); // 输出:16

3. Class的语法

ES6中Class的语法相对简洁明了。一个Class可以包含构造函数、属性和方法等。下面介绍一些常用的语法规则:

3.1 构造函数

在Class中使用constructor关键字定义构造函数。构造函数用于创建对象时进行初始化操作,通过new关键字实例化类时会自动调用构造函数。

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

构造函数中的this关键字表示当前实例化的对象。

3.2 属性

在Class中可以定义各种属性。属性可以直接定义在Class的内部,也可以在构造函数中通过this关键字进行定义。

  1. class Rectangle {
  2. width = 0; // 直接定义属性
  3. height = 0;
  4. constructor(width, height) {
  5. this.width = width; // 在构造函数中定义属性
  6. this.height = height;
  7. }
  8. }

3.3 方法

在Class中定义的函数称为方法。可以直接在Class的内部定义方法,也可以使用ES6的简写形式。

  1. class Rectangle {
  2. constructor(width, height) {
  3. this.width = width;
  4. this.height = height;
  5. }
  6. area() {
  7. // 定义方法
  8. return this.width * this.height;
  9. }
  10. perimeter() {
  11. return 2 * (this.width + this.height);
  12. }
  13. }

3.4 方法的访问修饰符

在Class中,可以使用访问修饰符来限制方法的访问权限。ES6中的Class默认所有方法都是公共的,可以被外部调用。但我们可以使用staticgetsetprivateprotected等修饰符来控制方法的访问。

  • static:定义静态方法,只能通过类本身调用,不能通过类的实例调用。
  • getset:定义属性的读取和设置方法,使用类似访问属性的语法进行调用。
  • private:定义私有方法,只能在类的内部被访问,外部无法访问。
  • protected:定义受保护方法,只能在类的内部和子类中被访问,外部无法访问。

    class Rectangle {

    static description = ‘This is a rectangle’; // 静态属性

    constructor(width, height) {

    1. this.width = width;
    2. this.height = height;

    }

    static createSquare(side) {

    1. // 静态方法
    2. return new Rectangle(side, side);

    }

  1. get area() {
  2. // Getter方法
  3. return this.width * this.height;
  4. }
  5. set area(value) {
  6. // Setter方法
  7. this.width = Math.sqrt(value);
  8. this.height = Math.sqrt(value);
  9. }
  10. #privateMethod() {
  11. // 私有方法
  12. console.log('This is a private method');
  13. }
  14. protectedMethod() {
  15. // 受保护方法
  16. console.log('This is a protected method');
  17. }
  18. publicMethod() {
  19. // 公共方法
  20. console.log('This is a public method');
  21. this.#privateMethod();
  22. this.protectedMethod();
  23. }
  24. }

在上述示例中,我们定义了一个Square类,它继承自Rectangle类。通过super关键字调用父类的构造函数,确保父类的属性被正确初始化。子类可以新增或覆盖父类的方法。

  1. const square = new Square(5);
  2. console.log(square.area()); // 输出:25
  3. console.log(square.perimeter()); // 输出:20

4. 类的静态方法和属性

静态方法和属性属于类本身,而不是类的实例。静态方法和属性可以通过类名直接访问,无需实例化类。

  1. class MathUtil {
  2. static PI = 3.14159; // 静态属性
  3. static square(number) {
  4. // 静态方法
  5. return number * number;
  6. }
  7. }

在上述示例中,我们定义了一个MathUtil类,它具有一个静态属性PI和一个静态方法square()。可以通过类名直接访问静态属性和方法。

  1. console.log(MathUtil.PI); // 输出:3.14159
  2. console.log(MathUtil.square(5)); // 输出:25

5. Getter和Setter方法

Getter和Setter方法用于对类的属性进行读取和设置操作,可以通过类似访问属性的语法进行调用。

  1. class Circle {
  2. constructor(radius) {
  3. this.radius = radius;
  4. }
  5. get diameter() {
  6. return 2 * this.radius;
  7. }
  8. set diameter(value) {
  9. this.radius = value / 2;
  10. }
  11. }

在上述示例中,我们定义了一个Circle类,它具有一个属性radius。通过定义get diameter()方法和set diameter()方法,我们可以通过类似访问属性的方式来读取和设置直径(diameter)属性,而不需要直接访问radius属性。

  1. const circle = new Circle(5);
  2. console.log(circle.diameter); //
  3. 输出:10
  4. circle.diameter = 12;
  5. console.log(circle.radius); // 输出:6

6. 类的私有属性和方法

在ES6中,可以使用#作为前缀来定义私有属性和方法。私有属性和方法只能在类的内部被访问,外部无法访问。

  1. class Person {
  2. #name; // 私有属性
  3. constructor(name) {
  4. this.#name = name;
  5. }
  6. #privateMethod() {
  7. // 私有方法
  8. console.log('This is a private method');
  9. }
  10. publicMethod() {
  11. // 公共方法
  12. console.log(`Hello, my name is ${
  13. this.#name}`);
  14. this.#privateMethod();
  15. }
  16. }

在上述示例中,我们定义了一个Person类,它具有一个私有属性#name和一个私有方法#privateMethod()。私有属性和方法只能在类的内部访问。

  1. const person = new Person('John');
  2. person.publicMethod(); // 输出:Hello, my name is John
  3. person.#name; // 报错:SyntaxError: Private field '#name' must be declared in an enclosing class
  4. person.#privateMethod(); // 报错:SyntaxError: Private field '#privateMethod' must be declared in an enclosing class

7. 类的实例和构造函数

在ES6中,类的实例通过new关键字进行创建,并自动调用类的构造函数进行初始化。

  1. const rect = new Rectangle(5, 3);
  2. console.log(rect.area()); // 输出:15
  3. console.log(rect.perimeter()); // 输出:16

可以使用instanceof运算符来判断一个对象是否是某个类的实例。

  1. console.log(rect instanceof Rectangle); // 输出:true
  2. console.log(rect instanceof Object); // 输出:true

8. 类的继承

继承是面向对象编程中的重要概念之一,它允许我们创建一个基类(父类),其他类可以继承该基类并扩展或覆盖其中的属性和方法。ES6中使用extends关键字实现类的继承。

  1. class Square extends Rectangle {
  2. constructor(side) {
  3. super(side, side); // 调用父类的构造函数
  4. }
  5. }

9. 类的封装

封装通过将数据和操作数据的方法封装在一个对象中,实现了数据的保护和访问的控制。类的属性和方法可以使用不同的访问修饰符来控制其可见性。

  1. class Rectangle {
  2. #width; // 私有属性
  3. #height;
  4. constructor(width, height) {
  5. this.#width = width;
  6. this.#height = height;
  7. }
  8. getArea() {
  9. // 公共方法
  10. return this.#width * this.#height;
  11. }
  12. }
  13. const rect = new Rectangle(5, 3);
  14. console.log(rect.#width); // 报错:SyntaxError: Private field '#width' must be declared in an enclosing class
  15. console.log(rect.getArea()); // 输出:15

在上述示例中,Rectangle类具有私有属性#width和#height,只能在类的内部被访问。通过定义公共方法getArea()来访问私有属性,从而实现了封装。

10. 类的多态

多态允许不同的对象对相同的消息作出不同的响应。通过继承和方法的覆盖,不同的子类可以对父类的方法进行不同的实现,从而实现多态性。

  1. class Animal {
  2. makeSound() {
  3. console.log('Animal makes sound');
  4. }
  5. }
  6. class Dog extends Animal {
  7. makeSound() {
  8. console.log('Dog barks');
  9. }
  10. }
  11. class Cat extends Animal {
  12. makeSound() {
  13. console.log('Cat meows');
  14. }
  15. }
  16. const animal = new Animal();
  17. const dog = new Dog();
  18. const cat = new Cat();
  19. animal.makeSound(); // 输出:Animal makes sound
  20. dog.makeSound(); // 输出:Dog barks
  21. cat.makeSound(); // 输出:Cat meows

在上述示例中,Animal类是基类,Dog和Cat类是子类。它们都具有makeSound()方法,但不同的子类对该方法进行了不同的实现,实现了多态性。

通过封装、继承和多态,面向对象编程提供了一种更加灵活和可扩展的编程方式,使得代码的组织和管理更加直观和高效。

11. 结语

ES6引入的Class机制为JavaScript提供了一种更直观、更简洁的面向对象编程方式。通过Class,我们可以更方便地定义和使用类,实现封装、继承和多态等面向对象编程的基本原理。同时,ES6还提供了许多其他的语法糖和特性,使得JavaScript在面向对象编程方面更加强大和灵活。

12. 参考资料

  • MDN Web Docs - Classes
  • ECMAScript 6 入门 - Class
  • Understanding ECMAScript 6 - Classes
  • Exploring ES6 - Classes

发表评论

表情:
评论列表 (有 0 条评论,148人围观)

还没有评论,来说两句吧...

相关阅读