JavaScript的class本质是构造函数的语法糖,但存在关键差异:不提升、强制super()调用、原生支持static和#私有字段、new.target行为更严格,且仍基于原型链。
JavaScript 的 class 本质是构造函数的语法糖,不是新机制,但写法、语义和行为有关键差异——尤其在继承、静态方法、私有字段和初始化时机上。
这是最常踩的坑:你不能在声明前使用 class,否则报 ReferenceError: Cannot access 'MyClass' before initialization;但

function Person() {} 可以在声明前调用(函数提升)。
class 声明处于「暂时性死区」(TDZ),必须先声明再使用const MyClass = class { ... }(类表达式),它和函数表达式一样不提升,但赋值后才可访问用 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
虽然可以用 Person.staticMethod = function() {} 模拟静态方法,但 static 关键字让意图更清晰、继承关系更准确(子类可直接继承或覆盖 static 方法)。私有字段 #name 更是完全依赖 class 语法——传统函数构造器无法实现真正私有(只能靠闭包模拟,且无法在原型链上共享)。
static 方法不可被实例调用,只能通过类名调用,比如 MyClass.doSomething()
# 开头的字段和方法仅在类内部可访问,外部读写均抛 SyntaxError 或 TypeError
get #x() { return this.#_x } 合法,但 get #x() {} 不行)在 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 A {} 生成的仍是函数对象,A.prototype 依然存在,所有「语法糖」都映射到已有机制。混淆点往往出现在继承链调试、this 绑定丢失、或试图用 instanceof 判断类表达式结果时。别急着封装,先在控制台里打印几遍 A.prototype.constructor 和 Object.getPrototypeOf(new A()) 看看。