JavaScript的class本质是构造函数的语法糖,但存在关键差异:不提升、强制super()调用、原生支持static和#私有字段、new.target行为更严格,且仍基于原型链。

JavaScript 的 class 本质是构造函数的语法糖,不是新机制,但写法、语义和行为有关键差异——尤其在继承、静态方法、私有字段和初始化时机上。
class 声明不会被提升,而 function 构造函数会
这是最常踩的坑:你不能在声明前使用 class,否则报 ReferenceError: Cannot access 'MyClass' before initialization;但 function Person() {} 可以在声明前调用(函数提升)。
-
class声明处于「暂时性死区」(TDZ),必须先声明再使用 - 想提前使用?改用
const MyClass = class { ... }(类表达式),它和函数表达式一样不提升,但赋值后才可访问 - 构造函数写法更宽松,适合需要动态生成或条件定义的场景
constructor 必须显式调用 super() 才能继承父类实例属性
用 class 继承时,子类 constructor 中若定义了,就必须第一行调用 super();否则报 ReferenceError: Must call super constructor in derived class before accessing 'this'。而传统构造函数继承(如 Child.prototype = Object.create(Parent.prototype))不强制这一约束,但容易漏掉 this 初始化。
- 没写
constructor?class会自动插入空的,并隐式调用super() - 写了
constructor却忘了super()?运行时直接报错,无法绕过 - 传参给
super()是为了让父类构造函数设置this上的初始属性,漏掉会导致this.xxx为undefined
static 方法和 # 私有字段只在 class 语法中原生支持
虽然可以用 Person.staticMethod = function() {} 模拟静态方法,但 static 关键字让意图更清晰、继承关系更准确(子类可直接继承或覆盖 static 方法)。私有字段 #name 更是完全依赖 class 语法——传统函数构造器无法实现真正私有(只能靠闭包模拟,且无法在原型链上共享)。
立即学习“Java免费学习笔记(深入)”;
-
static方法不可被实例调用,只能通过类名调用,比如MyClass.doSomething() -
#开头的字段和方法仅在类内部可访问,外部读写均抛SyntaxError或TypeError - 私有字段目前不支持私有 getter/setter 的简写(
get #x() { return this.#_x }合法,但get #x() {}不行)
new.target 在 class 构造器中行为更严格
在 class 的 constructor 中,new.target 总是指向当前被 new 调用的类(包括子类),可用于限制类不能被直接调用(如抽象基类)。而普通函数中,new.target 可能为 undefined(非 new 调用)或指向构造函数本身,判断逻辑更松散。
- 想禁止类被直接调用?
if (!new.target) throw new Error('Cannot call without new') - 想确保只能被继承?
if (new.target === MyClass) throw new Error('MyClass is abstract') - 这个模式在构造函数里也能做,但 class 提供了更自然的上下文和错误定位
真正难的不是写 class,而是理解它背后仍跑在原型链上——class A {} 生成的仍是函数对象,A.prototype 依然存在,所有「语法糖」都映射到已有机制。混淆点往往出现在继承链调试、this 绑定丢失、或试图用 instanceof 判断类表达式结果时。别急着封装,先在控制台里打印几遍 A.prototype.constructor 和 Object.getPrototypeOf(new A()) 看看。











