跳到主要内容

class:它到底是不是“语法糖”?和原型、this、继承的关系是什么?

面试速答(30 秒版 TL;DR)

  • JS 的 class 本质上仍是**原型(prototype)**模型的语法糖:方法挂在 ClassName.prototype 上,实例通过原型链查找。
  • class 的方法默认是非枚举的(和手写 prototype 不同点之一),class 体内默认是严格模式。
  • class 声明有 TDZ(不像 function 声明那样可提前用),但面试更常问“为什么访问前会报错”:因为它是块级绑定。
  • 继承(extends)本质是:子类原型链指向父类原型,并且子类构造会通过 super() 初始化父类部分;在子类构造里 必须先 super() 才能用 this

心智模型:两条链

  • 原型链:解决“属性/方法从哪找”(继承)
  • 调用点规则:解决“this 指向谁”(运行时绑定)

相关文档:


class 与 prototype:一眼看穿

class A {
foo() {
return 1;
}
}

const a = new A();
console.log(a.foo()); // 1
console.log(a.__proto__ === A.prototype); // true

等价理解(简化):

  • foo 不是放在实例上,而是放在 A.prototype
  • 实例通过原型链找到 foo

类字段 vs 原型方法:this 绑定差异很关键

class C1 {
constructor(x) {
this.x = x;
}
getX() {
return this.x;
}
}

class C2 {
constructor(x) {
this.x = x;
}
getX = () => {
return this.x;
};
}

const c1 = new C1(1);
const c2 = new C2(1);

const f1 = c1.getX;
const f2 = c2.getX;

// f1(); // this 丢失(严格模式 TypeError)
console.log(f2()); // 1(箭头函数捕获实例 this)

面试口述:

  • 原型方法:共享,省内存,但方法被“拿出来”会丢 this
  • 字段箭头函数:每实例一份,耗内存,但天然绑定 this

继承:extends 与 super 的本质

class Base {
constructor() {
this.base = 1;
}
}

class Sub extends Base {
constructor() {
super();
this.sub = 2;
}
}

典型题 & 标准答法

Q1:class 是语法糖吗?有哪些“不是糖”的点?

答法建议:

  • 语义层面仍是 prototype
  • class 有更严格的语义约束:TDZ、默认严格模式、必须 new 调用、方法不可枚举等,使代码更可预测

Q2:为什么子类构造里必须先 super()

因为子类实例 this 的初始化由父类构造完成(规范层面要求先创建并初始化 this),在 super() 前访问 this 会报错。


易错点/坑

  • 不能把类当普通函数直接调用:A() 会报错,必须 new A()
  • 把原型方法当回调传递时 this 容易丢,需要 bind 或用字段箭头函数。

速记要点(可背诵)

  • class 还是 prototype;实例方法在 Class.prototype
  • 子类构造先 super(),再用 this
  • 原型方法省内存但易丢 this;字段箭头函数稳定但每实例一份。