js中class的深入探讨

276 阅读6分钟

关于 JavaScript 中的 class,我们可以从以下几个方面进行深入探讨:

  • 基本语法和定义:

    class ClassName {
      constructor(arg1, arg2) {
        this.arg1 = arg1;
        this.arg2 = arg2;
      }
      
      method1() {
        // 方法实现
      }
    }
    
  • 继承 (extends 和 super) :

    • 使用 extends 关键字来创建子类。
    • 必须调用 super() 来初始化父类中的构造器。
    class Parent {
      constructor(value) {
        this.value = value;
      }
    }
    
    class Child extends Parent {
      constructor(value, name) {
        super(value); // 调用父类构造器
        this.name = name;
      }
    }
    
  • 静态方法 (static) :

    • 可以在类中定义静态方法,这些方法不能通过实例访问,只能通过类本身调用。
    class MyClass {
      static myStaticMethod() {
        return 'Hello from static method!';
      }
    }
    console.log(MyClass.myStaticMethod());
    
  • 私有属性和方法 (#) :

    • ES2022 引入了对私有属性的支持,允许在类内部定义私有成员。在JavaScript中,私有属性和方法是由#符号标识的,它们不能从类的外部直接访问。如果尝试从外部访问或修改这些私有成员,将会导致引用错误。但是,可以通过类提供的公共方法间接地修改私有成员
    class Example {
      #privateField = "This is private";
      
      setPrivateValue(newValue) { 
        this.#privateField = newValue; 
      }
      
      getPrivateValue() {
        return this.#privateField;
      }
    }
    
    const instance = new Example(); 
    instance.setPrivateValue(10); 
    console.log(instance.getPrivateValue()); // 输出 10
    
  • getter 和 setter:

    • 类可以定义 getter 和 setter 来控制属性的读取和设置行为。
    class Person {
      constructor(name) {
        this._name = name;
      }
    
      get name() {
        return this._name.toUpperCase();
      }
    
      set name(newName) {
        if (newName.length > 0) {
          this._name = newName;
        }
      }
    }
    
  • 类表达式 vs 类声明:

    • 类表达式允许你在运行时定义类,并且可以赋值给变量或作为参数传递。
    • 类声明则是直接在文件作用域中定义一个类。

class和构造函数(Constructor Functions)的区别

构造函数(Constructor Functions)

构造函数是一种传统的面向对象编程方式,在ES6之前的版本中广泛使用。构造函数本质上是一个普通的JavaScript函数,但它被用来创建新的对象实例。

特点:
  1. 函数声明提升:构造函数可以被提升,这意味着可以在声明之前调用。
  2. 原型链:构造函数可以使用prototype属性来定义共享的方法和属性。
  3. 灵活的实例化:构造函数可以用new关键字来实例化,也可以像普通函数一样调用(虽然这通常会导致意外的结果)。
  4. 非严格模式:默认情况下,构造函数不在严格模式下执行。
  5. 属性可枚举性:通过构造函数添加到prototype上的属性和方法默认是可以枚举的。

示例:

function Person(name) {
  this.name = name;
}

Person.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name}`);
};

const person = new Person('Alice');
person.greet(); // Hello, my name is Alice

类(Classes)

类是ES6引入的一种新的面向对象编程方式,它提供了更接近于传统面向对象语言(如Java或C#)的语法糖。

特点:
  1. 没有函数声明提升:类定义不会被提升,所以必须在使用之前定义。
  2. 严格模式:类内部默认使用严格模式。
  3. 强制使用new:类必须通过new关键字来实例化,否则会抛出错误。
  4. 原型属性不可枚举:类中的方法默认是不可枚举的,即它们不会出现在for...in循环中。
  5. 私有属性和方法:ES2022引入了私有属性(使用#符号),这是构造函数所不具备的功能。
  6. 继承简化:类的继承通过extends关键字和super调用父类构造器,使得继承更加简洁易懂。

示例:

javascript
class Person {
  constructor(name) {
    this.name = name;
  }

  greet() {
    console.log(`Hello, my name is ${this.name}`);
  }
}

const person = new Person('Alice');
person.greet(); // Hello, my name is Alice

总结

尽管class 和构造函数都可以用来实现面向对象编程,但class 提供了一种更现代、更简洁的方式来定义类,并且更符合面向对象编程的语言习惯。构造函数则提供了更大的灵活性,但在使用时需要更加小心,尤其是在处理原型链和继承时。根据项目的需求和个人偏好,可以选择适合的方式。 将一个类转换为构造函数涉及几个步骤,主要是将类中的构造函数、方法和继承关系转换为相应的构造函数形式。下面是一个详细的步骤说明,包括如何转换类中的构造函数、方法和继承关系。

类(class)转换成 构造函数

示例类

假设我们有一个简单的类定义:

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
  }
}

步骤 1: 转换构造函数

首先,将类的构造函数转换为构造函数形式:

function Person(name, age) {
  this.name = name;
  this.age = age;
}

步骤 2: 添加方法

将类中的方法添加到构造函数的原型上:

Person.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};

步骤 3: 继承

如果类中涉及到继承,也需要将继承关系转换为构造函数形式。例如,假设我们有一个子类 Student 继承自 Person

class Student extends Person {
  constructor(name, age, grade) {
    super(name, age);
    this.grade = grade;
  }

  study() {
    console.log(`${this.name} is studying.`);
  }
}

转换继承

将继承关系转换为构造函数形式:

function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};

function Student(name, age, grade) {
  Person.call(this, name, age); // 调用父类构造函数
  this.grade = grade;
}

// 设置原型链
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

Student.prototype.study = function() {
  console.log(`${this.name} is studying.`);
};

完整示例

将上述所有部分组合起来:

// Person 构造函数
function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};

// Student 构造函数
function Student(name, age, grade) {
  Person.call(this, name, age); // 调用父类构造函数
  this.grade = grade;
}

// 设置原型链
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

Student.prototype.study = function() {
  console.log(`${this.name} is studying.`);
};

// 创建实例
const alice = new Person('Alice', 25);
alice.greet(); // Hello, my name is Alice and I am 25 years old.

const bob = new Student('Bob', 20, 'Sophomore');
bob.greet(); // Hello, my name is Bob and I am 20 years old.
bob.study(); // Bob is studying.

结论

通过以上步骤,你可以将一个类转换为构造函数形式,并保留其构造函数、方法和继承关系。这种方法在ES5及以前的版本中非常常见,而在现代JavaScript中,使用类更加简洁和直观。不过,在某些场景下,构造函数形式仍然是有用的,特别是在需要兼容旧版本浏览器的情况下。

类(class)和普通函数的区别

在JavaScript中,类(class)和普通函数(functions)之间有几个关键的区别,这些区别主要体现在语法糖、执行上下文、继承等方面。下面是类和普通函数的一些主要区别:

语法糖

  • :类提供了一种更接近于传统面向对象语言(如Java或C++)的语法糖,使定义类和继承更加直观。
  • 普通函数:普通函数没有专门的面向对象语法,通常用于定义过程或函数式编程中的函数。

执行上下文

  • :类内部默认处于严格模式,即使整个脚本没有显式启用严格模式。
  • 普通函数:普通函数如果没有显式声明为'use strict',则默认不在严格模式下执行。

实例化

  • :类必须通过new关键字来实例化对象,否则会抛出错误。
  • 普通函数:普通函数既可以作为构造函数通过new关键字来实例化对象,也可以作为普通函数直接调用。

属性和方法

  • :类的方法默认是不可枚举的,这意味着它们不会出现在for...in循环中。
  • 普通函数:普通函数定义的方法默认是可枚举的,除非明确设置为不可枚举。

私有成员

  • :ES2022引入了私有字段和方法,使用#符号来标记私有成员。
  • 普通函数:普通函数没有内置的私有成员概念,通常通过闭包或其他技巧来模拟私有成员。

继承

  • :类使用extends关键字来实现继承,并且使用super关键字来调用父类的构造函数。
  • 普通函数:普通函数通过原型链来实现继承,通常使用Object.create()来设置原型。

提升

  • :类声明不会被提升,必须在声明之后才能使用。
  • 普通函数:函数声明会被提升,可以在声明之前调用。