class 是糖?还是坑?——从一个小例子看懂 JS 原型与类编程本质

426 阅读3分钟

你以为你写了 class,实际上是 prototype 在工作。

一、一段看起来正常的 class 代码

下面是一个简单的类,用来统计按钮点击次数:

class Counter {
  count = 0;
  inc() {
    this.count++;
    console.log('当前 count:', this.count);
  }
}

const counter = new Counter();
const fn = counter.inc.bind(counter); // 关键:绑定 this
fn(); // ✅ 输出:当前 count: 1

看起来没有问题,但如果你写成:

const fn = counter.inc;
fn(); // ❌ TypeError: Cannot read properties of undefined

就会达到一个初学者常见的坑:this 丢失问题

二、问题本质:class 里的方法本质是 prototype 上的

class Counter {
  count = 0;
  inc() {
    this.count++;
  }
}

但实际上,类里的 inc() 方法,等同于写在原型上:

Counter.prototype.inc

所以当你写成:

const fn = counter.inc;
fn();

你操作的是 Counter.prototype.inc(),它被调用时的 this,并不是 counter

三、ES2020 的 class:私有字段和私有方法的加入

ES2020 引入了 # 前缀的私有字段和方法,它们不会存在于 prototype 上,而是存储在每个实例的私有槽中:

class Counter {
  #count = 0; // 私有字段
  #log() {    // 私有方法
    console.log('当前 count:', this.#count);
  }

  inc() {
    this.#count++;
    this.#log();
  }
}

这些私有成员:

  • ❌ 无法从外部访问
  • ❌ 不存在于 prototype 上
  • ✅ 更安全,封装性更强

但注意:类中的普通方法(如 inc() )仍然在 prototype 上,只有 #私有 的才在实例内部。

所以你可以理解为:

class 是对原型机制的封装升级,#私有 是对封装性的增强补丁。

四、热门问题:class 里的 arrow function 有用吗?

有人会说:不如写成箭头函数,这样 this 就绑定了:

class Counter {
  count = 0;
  inc = () => {
    this.count++;
  }
}

确实可行,但他是属于对象本身,而非 prototype,每创建一个对象,都会重复一份 inc 方法。

写法内存开销是否在 prototype 上this 安全
普通方法✅ 小✅ 是❌ 可能丢失
箭头函数属性❌ 大❌ 否✅ 永远安全

五、实际应用中,你应该怎么写

想规范写法:绑定 this

class Counter {
  count = 0;
  constructor() {
    this.inc = this.inc.bind(this);
  }
  inc() {
    this.count++;
  }
}

想便捷写法:直接箭头函数

class Counter {
  count = 0;
  inc = () => {
    this.count++;
  }
}

不想把方法抽离 context:直接调用

counter.inc();

六、class 真的是坑吗?

class 本质是 function + prototype 的糖装。

它不是坑,当你明白了它的原理时,很好用:

  • 用来类型分层
  • 用来构造构件库 API
  • 用来作为 DSL 配置的配置器

但当你不明白 prototype 时,用 class 就可能成为坑

  • this 丢失
  • super 指向混乱
  • 继承时 prototype chain 未明确

🧠 什么是“非类化语言”?

不是说这个语言没有类(如 JS 后来也支持 class),而是指:

这个语言的底层并不依赖 class 来实现封装、继承、组合与复用。

JavaScript 就是典型代表 —— 它底层基于原型链(prototype chain)和函数闭包,不是 class。

七、总结:看懂了这个 class Counter,你就明白了 JS 的编程本质

JS 不高级吗?是因为“自由”让人误判了。JavaScript 不是“不高级”,而是它: 允许你写得很烂,也允许你写得极致优雅。

  • 你可以用 class 模拟面向对象
  • 你也可以用闭包模拟封装
  • 你还可以用高阶函数、柯里化、组合函数写函数式代码

这是一种 “可进可退、无框架依赖的原生表达力”。不是写 class 就是面向对象; 也不是用 prototype 就是老时代。

真正强大的前端工程师,是知道:

在哪些场景下,该用 class;在哪些场景下,应该先用组合和函数