Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

面试官:面向对象编程与Class #28

Open
linwu-hi opened this issue Jul 30, 2023 · 0 comments
Open

面试官:面向对象编程与Class #28

linwu-hi opened this issue Jul 30, 2023 · 0 comments

Comments

@linwu-hi
Copy link
Owner

面向对象编程与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示例如下:

class Rectangle {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }

  area() {
    return this.width * this.height;
  }

  perimeter() {
    return 2 * (this.width + this.height);
  }
}

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

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

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

3. Class的语法

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

3.1 构造函数

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

class Rectangle {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }
}

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

3.2 属性

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

class Rectangle {
  width = 0;    // 直接定义属性
  height = 0;

  constructor(width, height) {
    this.width = width;    // 在构造函数中定义属性
    this.height = height;
  }
}

3.3 方法

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

class Rectangle {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }

  area() {            // 定义方法
    return this.width * this.height;
  }

  perimeter() {
    return 2 * (this.width + this.height);
  }
}

3.4 方法的访问修饰符

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

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

  constructor(width, height) {
    this.width = width;
    this.height = height;
  }

  static createSquare(side) {     // 静态方法
    return new Rectangle(side, side);
  }



  get area() {           // Getter方法
    return this.width * this.height;
  }

  set area(value) {     // Setter方法
    this.width = Math.sqrt(value);
    this.height = Math.sqrt(value);
  }

  #privateMethod() {    // 私有方法
    console.log('This is a private method');
  }

  protectedMethod() {   // 受保护方法
    console.log('This is a protected method');
  }

  publicMethod() {      // 公共方法
    console.log('This is a public method');
    this.#privateMethod();
    this.protectedMethod();
  }
}

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

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

4. 类的静态方法和属性

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

class MathUtil {
  static PI = 3.14159;    // 静态属性

  static square(number) {    // 静态方法
    return number * number;
  }
}

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

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

5. Getter和Setter方法

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

class Circle {
  constructor(radius) {
    this.radius = radius;
  }

  get diameter() {
    return 2 * this.radius;
  }

  set diameter(value) {
    this.radius = value / 2;
  }
}

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

const circle = new Circle(5);
console.log(circle.diameter);     //

 输出:10
circle.diameter = 12;
console.log(circle.radius);       // 输出:6

6. 类的私有属性和方法

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

class Person {
  #name;   // 私有属性

  constructor(name) {
    this.#name = name;
  }

  #privateMethod() {   // 私有方法
    console.log('This is a private method');
  }

  publicMethod() {      // 公共方法
    console.log(`Hello, my name is ${this.#name}`);
    this.#privateMethod();
  }
}

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

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

7. 类的实例和构造函数

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

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

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

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

8. 类的继承

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

class Square extends Rectangle {
  constructor(side) {
    super(side, side);    // 调用父类的构造函数
  }
}

9. 类的封装

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

class Rectangle {
  #width;  // 私有属性
  #height;

  constructor(width, height) {
    this.#width = width;
    this.#height = height;
  }

  getArea() {    // 公共方法
    return this.#width * this.#height;
  }
}

const rect = new Rectangle(5, 3);
console.log(rect.#width);  // 报错:SyntaxError: Private field '#width' must be declared in an enclosing class
console.log(rect.getArea());  // 输出:15

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

10. 类的多态

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

class Animal {
  makeSound() {
    console.log('Animal makes sound');
  }
}

class Dog extends Animal {
  makeSound() {
    console.log('Dog barks');
  }
}

class Cat extends Animal {
  makeSound() {
    console.log('Cat meows');
  }
}

const animal = new Animal();
const dog = new Dog();
const cat = new Cat();

animal.makeSound();  // 输出:Animal makes sound
dog.makeSound();     // 输出:Dog barks
cat.makeSound();     // 输出:Cat meows

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

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

11. 结语

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

12. 参考资料

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant