前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
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 热点
旧版
首页程序员面试题库setTimeout 回调 this 丢失 解决方法
JavaScript函数与作用域

把对象方法传给 setTimeout,为什么 this 就丢了?怎么解决?

this 不是函数自带的,而是看调用时怎么写。写 obj.method() 时 this 才是 obj;把 obj.method 传给 setTimeout,传过去的只是函数本身,定时器之后单独调用它,obj 就不在了。修法是用 bind(obj) 固定住,或者包一层 () => obj.method()。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#函数与作用域
先看核心答案读代码示例
理解线索

丢的是调用时点号前面的对象

  1. 方法调用obj.run() 调用时,点号前面的 obj 就是 this
  2. 单独调用const run = obj.run 之后调用 run(),没有点号,this 就不是 obj
  3. 修复bind 生成固定 this 的新函数,箭头函数则在回调里重新写出 obj.run()

这和异步无关。同步代码里 const fn = obj.method; fn() 一样会丢,setTimeout 只是最常见的出场位置。

核心回答

先记住这个答案

普通函数的 this 是调用时才决定的,写成 obj.method() 调用,点号前面的 obj 才是 this。setTimeout(obj.method, 1000) 只是把函数本身交给了定时器,obj 没有跟着传过去。一秒后定时器单独调用这个函数,this 就不再是 obj 了:浏览器里是 window,Node.js 里是定时器对象,严格模式下自己单独调用则是 undefined。修法有两种:setTimeout(obj.method.bind(obj)) 把 this 固定住,或者 setTimeout(() => obj.method()) 在回调里重新按方法调用。

  • this 看调用时的写法,不看函数定义在哪个对象上
  • 传 obj.method 只传了函数,obj 没跟过去
  • 不只是 setTimeout,解构、数组回调、事件回调都会丢
  • 修法:bind、箭头函数包一层、类字段箭头函数

传出去的只是函数

下面用一个 run 函数模拟「把回调交给别人,之后再调用」,这正是 setTimeout、事件系统对回调做的事。

直接传方法 vs 两种修法JavaScript
'use strict'
const user = {
  name: 'Ada',
  greet() {
    return this ? this.name : 'this 是 undefined'
  }
}
const run = (callback) => console.log(callback()) // 像定时器一样单独调用回调

run(user.greet) // 只传了函数
run(user.greet.bind(user)) // this 固定成 user
run(() => user.greet()) // 回调里重新写出 user.greet()
查看输出与解释
this 是 undefined
Ada
Ada

user.greet 这个表达式取到的就是一个函数,和 const fn = user.greet 拿到的一模一样。函数本身不记得自己是从哪个对象上取下来的。run 里调用 callback() 时前面没有点号,严格模式下 this 就是 undefined。 bind(user) 返回一个新函数,不管以后怎么调用,this 都是 user。箭头函数的写法则是把「方法调用」这个动作本身包进回调:回调执行时,重新执行了一次 user.greet(),点号前面又有了 user。

三种修法怎么选

bind 和箭头函数都能解决问题,区别在细节上。

bind、箭头包装和类字段
bind 在调用 bind 那一刻就固定了对象和预置参数,得到一个稳定的函数引用,适合需要保存下来、以后解绑的场景,比如 addEventListener 和 removeEventListener 要用同一个引用。 箭头函数包一层,是在回调真正执行时才去读 user 这个变量,读到的是那时候的对象,也方便顺手多传几个参数,比如 () => user.greet('hi')。它每写一次就是一个新函数,同样不能拿来解绑。 在类里还有第三种写法:把方法写成类字段箭头函数,greet = () => { ... }。每个实例创建时都会生成一个 this 固定为该实例的函数,传到哪里都不会丢。React 类组件处理 onClick 常这么写。代价是函数挂在每个实例上而不是原型上,实例多了会多占内存,子类也不能用 super.greet() 调用它。

不只是 setTimeout

只要把方法「取下来」再交出去,都会遇到同样的问题。比如 arr.forEach(obj.handle),forEach 调用回调时没有点号前的对象;const { greet } = user 解构出来再调用;把 this.handleClick 直接交给事件监听。解决思路都一样:要么 bind,要么包一层箭头函数,forEach 还可以用第二个参数 thisArg 指定 this。

回答前,多想一步

容易答错的地方

以为是异步导致的
很多人说「因为 setTimeout 是异步的,所以 this 丢了」。其实同步代码里 const fn = obj.method; fn() 一样会丢,真正的原因是调用时前面没有了 obj.,和执行早晚无关。
以为定时器回调里 this 一定是 undefined
这取决于调用它的一方。浏览器的 setTimeout 会主动把 window 作为 this 传给回调,所以即使在严格模式的代码里,this 也是 window,此时 this.name 读到的是 window.name,常常是个空字符串,不报错却拿错数据,更难发现。Node.js 里 this 则是返回的那个 Timeout 对象。
在 render 里每次都 bind
在 React 的 render 里写 onClick={this.handle.bind(this)},每次渲染都会生成新函数,子组件用 memo 做的浅比较会失效。应该在构造函数里 bind 一次,或者改用类字段箭头函数。
试着用自己的话回答

面试官还会怎么问?

对箭头函数用 bind 能改变 this 吗?

不能。箭头函数没有自己的 this,用的是定义位置外层的 this,bind、call、apply 传的 this 都会被忽略,只有参数能传进去。

setTimeout 能不能直接传参数给回调?

可以。从第三个参数开始,会原样传给回调,比如 setTimeout(fn, 1000, a, b)。但它只能传参数,不能指定 this,所以解决不了 this 丢失的问题。

事件监听里是该用 bind 还是用 event.currentTarget?

看你需要什么。addEventListener 调用普通函数时,this 本来就是绑定的元素,此时 bind 会把它覆盖掉。如果回调里既要用组件实例又要用元素,就 bind 实例或用箭头函数,元素改从 event.currentTarget 拿。

从一道题,走向一组知识

把知识连起来

函数与作用域

for 循环里用 var,setTimeout 为什么打印出来全是同一个数?

两题都是 setTimeout 回调里的经典坑,一个丢了 this,一个读到了循环结束后的变量值。

函数与作用域

闭包为什么会导致内存泄漏?怎么避免?

用 bind 或箭头函数修复后,回调会一直引用着对象,定时器和监听不清理就会造成内存泄漏。

参考资料

  • JavaScript:函数与作用域参考资料

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

本题目录
  1. 先记住这个答案
  2. 传出去的只是函数
  3. 三种修法怎么选
  4. 不只是 setTimeout
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

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

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