一、前言

提到 ES6 的 class,不少人觉得它是全新的面向对象工具,但其实它只是构造函数的语法糖——ES5 能实现的功能,class 基本都能做到,底层还是靠原型链在运行。

它的核心价值很简单:让原本零散的原型链写法变清晰,更贴近传统面向对象语言的风格,帮我们少花点心思在复杂语法上,多聚焦在代码逻辑本身。

下面就一起聊聊 class 到底怎么用。

二、原先的写法和class写法对比:

原来的代码:
function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.sayName = function() {
    return this.name;
}

const xiaoming = new Person('小明', 18);
console.log(xiaoming);

解释:

1.前面定义了一个构造函数Person,当使用new这个方法调用的时候,会创建一个新的对象,并且通过this将name和age添加道这个新对象上面。

2.原型方法sayName:实在Person的原型对象上添加了一个显得方法sayName,这样就能使所以普通过构造函数创建的实例比如小明都可以共用这个方法,

打印结果:

用ES6中的class实现的方法:
class Person {
    constructor(name, age) {
      this.name = name;
      this.age = age;
    }
  
    sayName() {
      return this.name;
    }
}
// 用 new 创建实例时,会自动调用 constructor
const xiaoming = new Person('小明', 18)
console.log(xiaoming);
// { name: '小明', age: 18 }

console.log((typeof Person));
// function
console.log(Person === Person.prototype.constructor);
// true

解释:

1.class 是一种语法糖,它的底层仍然是基于原型链的。其中 constructor 方法是类的构造函数,当使用 new 关键字创建类的实例时,constructor 方法会被自动调用,用于初始化实例的属性(name 和 age)。

2.在类体中定义的 sayName 方法,会被默认添加到类的原型对象(Person.prototype)上,和 ES5 中在 Person.prototype 上手动添加方法的效果是一致的,实例可以通过原型链调用到这个方法。

3.验证:通过控制台输出打印你会发现class关键字定义的类它的本质就是一个函数,进一步验证了class语法糖的身份。然后又验证了class底层基于原型链的本质。

三、constructor方法

一.基本方法

  1. 唯一性:一个类中只能有一个 constructor 方法。如果定义多个,会报错。

  2. 默认返回值constructor 默认返回实例对象本身(即 this)。如果手动返回一个对象,则实例会变成这个返回的对象(很少这么用);如果返回非对象(如基本类型),则忽略返回值,仍返回实例本身。

    class Test {
      constructor() {
        this.a = 1;
        return { b: 2 }; // 手动返回一个对象
      }
    }
    const t = new Test();
    console.log(t); // 输出:{ b: 2 }(实例变成了返回的对象)
    
  3. 必选性:如果类中没有显式定义 constructor,JavaScript 会自动添加一个默认的空构造器:

    class Person {
    }
    
    //相当于
    
    class Person {
       constructor() {}
    }
    const p = new Person(); // 正常执行,实例为空对象

二、继承中的constructor

当使用 extends 实现类的继承时,constructor 必须通过 super() 调用父类的构造函数,否则会报错。这是因为子类实例的创建需要先完成父类的初始化。

class Person {
  // 构造器:初始化实例属性
  constructor(name, age) {
    this.name = name; // 姓名
    this.age = age;   // 年龄
  }

  //定义方法 不需要function关键字,默认都是定义在原型上。
  sayHi() {
    console.log(`你好,我叫${this.name},今年${this.age}岁`);
  }
}

// 子类 Student 继承父类 Person
class Student extends Person {
  constructor(name, age, score) {
    super(name, age); // 调用父类构造器,初始化 name 和 age
    this.score = score; // 学生特有属性:成绩
  }
}

// 创建 Student 实例
let stu = new Student("张三", 20, 100);
console.log(stu); // 输出实例信息
stu.sayHi(); // 调用继承的方法

子类的 constructor 方法:

  • 必须先调用super(name, age):这里的name和age不能调换位置,super在这里代表父类的构造器,调用它会先执行 Person 的 constructor,给实例绑定 name 和 age(即学生先 “是人”,拥有人的基本属性)。
  • 注意:子类构造器中,super() 必须写在最前面(不能交换位置),否则会报错。因为子类实例的创建依赖父类的初始化。
  • 之后定义子类自己的属性 this.score = score(学生特有的 “成绩” 属性)。

四、静态方法

类是实例的原型,但是所有在类中定义的方法,都会被实例继承,那如何让方法不被实例继承呢?这个时候就只需要加上哟个关键字static关键字,就表示该方法不被实例继承,直接通过类来调用,这种方式就称为“静态方法”。

 <script>
      class MyClass {
        // 1. 用 static 关键字定义静态方法(属于类本身)
        static staticMethod() {
          return "我是类的静态方法,可通过类直接调用";
        }

        // 2. 静态方法也可以接收参数
        static calculateSum(a, b) {
          return a + b;
        }
      }
      //3. 通过继承的方法调用
      const StaticMethod = new MyClass();
      console.log(StaticMethod.staticMethod);// undefined 
      console.log(MyClass.staticMethod()); // 输出:"我是静态方法,通过类调用"

      // 4. 通过类名直接调用静态方法(无需 new 实例)
      console.log(MyClass.staticMethod()); // 输出:"我是类的静态方法,可通过类直接调用"
      console.log(MyClass.calculateSum(3, 5)); // 输出:8
    </script>

打印的结果:

这个例子就很好的证明了上面的观点:静态方法属于类,实例方法属于实例。

五、总结

  1. 本质:class 是构造函数 + 原型链的 “语法糖”,没新增机制,ES5 能实现的功能它都能做,底层还是靠原型链跑。
  2. 关键语法
    • constructor:创建实例时自动执行,用来初始化属性;继承时子类必须先写 super() 调用父类构造器。
    • 实例方法:没 static 的方法(如 sayHi()),实例能调用,共享在原型上。
    • 静态方法:加 static 的方法(如 staticMethod()),只能用类名调用,实例调不了。
  3. 继承:用 extends 实现,子类能复用父类属性 / 方法,新增属性前要先通过 super() 完成父类初始化。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐