前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库class 字段箭头函数方法与原型方法差异
JavaScript对象与原型

类字段箭头函数和原型方法有什么区别?

类字段里的箭头函数是实例自己的属性,每创建一个实例就会得到一份新的函数,并捕获该实例的 this。原型方法只有一份供实例共享,this 由调用方式决定。前者传给回调省去手动绑定,后者更适合普通方法和继承覆盖。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#对象与原型#性能优化
先看核心答案读代码示例
理解线索

两种方法放在不同位置

  1. 实例字段在创建实例时初始化,箭头函数捕获当时的 this
  2. 原型方法定义在原型上,多个实例共用同一函数
  3. 方法提取脱离对象调用时,原型方法可能失去原接收者

箭头函数字段的初始化发生在基类构造器正文前,派生类则发生在 super() 返回后。

核心回答

先记住这个答案

写成 handle = () => {} 的类字段会在每次创建实例时生成一个函数,作为实例自有属性保存。箭头函数捕获初始化时的 this,即使单独传给回调,仍指向原实例。写成 handle() {} 的方法位于类的原型上,多个实例共用同一个函数;它的 this 取决于调用方式。大量实例都要同一行为时,原型方法通常节省函数分配;需要稳定回调时,箭头函数字段更省心。

  • 箭头函数字段属于实例,每个实例各有一份
  • 原型方法由实例共享,调用时决定 this
  • 提取为回调时,箭头函数字段保留原实例
  • 大量实例默认优先考虑原型方法

看函数放在哪、是否共享

同一个类创建两个按钮模型,分别取出它们的方法比较。箭头函数字段每个实例会初始化一次,所以引用不同;原型方法只定义一次,所以引用相同。这并不意味着箭头函数一定造成可见的性能问题,但在大量实例上会多分配函数和闭包。

比较两个实例的方法引用JavaScript
class ButtonModel {
  onClick = () => this.label // 每个实例各建一份
  readLabel() { return this.label } // 定义在原型上
  constructor(label) { this.label = label }
}
const a = new ButtonModel('保存')
const b = new ButtonModel('取消')
console.log(a.onClick === b.onClick)
console.log(a.readLabel === b.readLabel)
查看输出与解释
false
true

可以再看属性位置:Object.hasOwn(a, 'onClick') 是 true,而 readLabel 要沿原型链查找。实例字段覆盖了同名原型属性时,普通的原型方法调用也可能被它遮住。理解存放位置后,内存和覆盖行为就容易推出来。

把方法交给回调时的差异

如果写 setTimeout(model.readLabel, 0),这个函数作为回调被调用时已脱离 model,不能再指望它的 this 自动指向原对象。类方法在严格模式下执行,独立调用时 this 可能是 undefined,读取属性就会报错。model.onClick 则是初始化时捕获了 model 的箭头函数,传出去仍能访问它的字段。

需要绑定时再选择写法JavaScript
class ButtonModel {
  onClick = () => this.label
  readLabel() { return this.label }
  constructor(label) { this.label = label }
}
const model = new ButtonModel('保存')
const arrowCallback = model.onClick // 已捕获实例
const boundCallback = model.readLabel.bind(model) // 显式绑定
console.log(arrowCallback())
console.log(boundCallback())
查看输出与解释
保存
保存

在组件事件、定时器等需要频繁传方法引用的地方,箭头函数字段能少写一次 bind()。但也要看到,手动 bind() 同样会创建新函数;性能比较应看创建实例的规模和实际调用方式,而不是只凭语法长短下结论。

继承与初始化时机

原型方法允许子类通过同名方法覆盖,也可以用 super.method() 调用父类实现。箭头函数字段是实例属性,不是原型方法;不能把它当作父类原型上的 super.method() 来访问。若子类也声明同名字段,还要注意父类与子类字段各自的初始化顺序,后初始化的字段会替换先前的实例属性。

基类的实例字段会在构造器正文之前初始化,派生类字段在 super() 返回后初始化。原稿说箭头函数字段“可以在构造函数前捕获 this”,容易误导为任何时候都能提前使用 this。对子类来说,super() 之前 this 还不可访问;字段箭头函数也不可能在此之前初始化。

回答前,多想一步

容易答错的地方

以为箭头函数写在类里就是原型方法
onClick = () => {} 使用的是类字段语法。它创建的是实例属性,两个实例的函数引用不同。真正的原型方法是 onClick() {}。两者在方法提取、共享和继承上都不同。
以为原型方法传出去后总能记住实例
原型方法没有自动绑定原实例。写 model.method() 时接收者是 model;单独传 model.method 后,调用方可能以别的方式调用它。需要稳定接收者时,用 bind()、包装箭头函数或实例箭头函数字段。
试着用自己的话回答

面试官还会怎么问?

给原型方法调用 bind() 后,还省内存吗?

原型上的原始函数仍由实例共享,但每次调用 bind() 都会产生新的绑定函数。如果每个实例都在构造器里绑定一次,分配开销就接近实例箭头函数字段;应按实际使用方式比较。

子类能用 super.onClick() 调父类的箭头函数字段吗?

不能按原型方法那样调用。父类箭头函数字段初始化在实例上,不在父类原型上;super.onClick 查找的是父类原型。需要可覆盖且可通过 super 复用的方法,应采用原型方法。

call() 能把箭头函数字段的 this 改成别的对象吗?

不能。箭头函数没有自己的动态 this 绑定;call()、apply() 或 bind() 传入的新接收者不会替换它已捕获的实例。原型方法则可以用这些方法显式指定接收者。

从一道题,走向一组知识

把知识连起来

对象与原型

class 里的 # 私有字段怎么写?它为什么是真正的私有?

类字段属于每个实例,私有字段也按实例初始化;另一题继续讨论字段的访问边界。

对象与原型

ES5 继承为什么要写 Child.prototype = new Parent()?Object.create 改进了什么?

原型方法由实例共享,ES5 的原型继承同样依赖这条原型链。

参考资料

  • JavaScript:对象与原型参考资料

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 看函数放在哪、是否共享
  3. 把方法交给回调时的差异
  4. 继承与初始化时机
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑