class:它到底是不是“语法糖”?和原型、this、继承的关系是什么?
面试速答(30 秒版 TL;DR)
- JS 的
class本质上仍是**原型(prototype)**模型的语法糖:方法挂在ClassName.prototype上,实例通过原型链查找。 class的方法默认是非枚举的(和手写prototype不同点之一),class体内默认是严格模式。class声明有 TDZ(不像function声明那样可提前用),但面试更常问“为什么访问前会报错”:因为它是块级绑定。- 继承(
extends)本质是:子类原型链指向父类原型,并且子类构造会通过super()初始化父类部分;在子类构造里 必须先super()才能用this。
心智模型:两条链
- 原型链:解决“属性/方法从哪找”(继承)
- 调用点规则:解决“
this指向谁”(运行时绑定)
相关文档:
- 原型链:prototype-chain
- 原型继承 vs class 继承:prototype-inheritance-vs-class-inheritance
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;字段箭头函数稳定但每实例一份。