前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
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 热点
旧版
首页程序员面试题库事件处理函数 this 指向 addEventListener
JavaScript函数与作用域

addEventListener 的回调里,this 指向谁?

用普通函数注册时,浏览器调用回调会把 this 设成挂监听器的元素,和 event.currentTarget 是同一个。箭头函数没有自己的 this,拿到的是定义时外层的 this。实际写代码建议直接用 event.currentTarget,不依赖 this。

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

事件回调里的三个「元素」

  1. this普通函数回调里,浏览器把它设成挂监听器的元素
  2. event.currentTarget当前正在执行监听器的元素,冒泡到哪一层就是哪一层
  3. event.target事件最初发生的元素,整个冒泡过程中不变

currentTarget 只在回调同步执行期间有值,派发结束后就变成 null。在 await 或 setTimeout 之后再读它,拿到的是 null。

核心回答

先记住这个答案

用普通函数给元素注册监听器,事件触发时浏览器调用这个函数,会把 this 设成挂监听器的那个元素,也就是 event.currentTarget。如果回调是箭头函数,箭头函数没有自己的 this,拿到的是定义它的地方外层的 this,在模块顶层是 undefined,在 class 方法里是实例,反正不会是元素。实际项目里更推荐直接读 event.currentTarget 和 event.target,意思更清楚,也不怕以后有人把回调改成箭头函数。

  • 普通函数回调里 this 等于 event.currentTarget
  • 箭头函数回调的 this 来自外层,不是元素
  • target 是点中的元素,currentTarget 是挂监听器的元素
  • 异步回调里 currentTarget 会变成 null,要先存下来

事件委托里对比 this、target 和 currentTarget

把监听器挂在列表 ul 上,点里面某个按钮里的图标。这时三个值各不相同,最能看出区别。

点击列表里按钮上的图标JavaScript
const list = document.querySelector('#list')

list.addEventListener('click', function (event) {
  console.log(this === list) // true:普通函数,this 是挂监听器的 ul
  console.log(this === event.currentTarget) // true
  console.log(event.target) // 点到的 <i> 图标,不是按钮

  const button = event.target.closest('button[data-id]')
  if (!button || !list.contains(button)) return
  console.log(button.dataset.id) // 真正要处理的按钮
})

list.addEventListener('click', (event) => {
  console.log(this === list) // false:箭头函数的 this 来自外层
})

事件从 <i> 开始冒泡,经过 button,到达 ul 时执行监听器。target 一直是最初点中的 <i>,currentTarget 和 this 是当前这一层 ul。做事件委托时要用 target.closest() 往上找到真正关心的按钮,再用 contains 确认它确实在这个列表里,防止找到列表外面的祖先元素。

为什么不同写法的 this 不一样

this 由函数怎么被调用决定。浏览器派发事件时,相当于用 listener.call(currentTarget, event) 调用你的普通函数,所以 this 就是当前元素。箭头函数不接受调用方传进来的 this,call 也改不了它。

其它几种注册方式
写在 HTML 里的 onclick="console.log(this)",以及 el.onclick = function () {},this 同样是这个元素。还有一种不太常见的写法:传一个带 handleEvent 方法的对象给 addEventListener,浏览器会调用 obj.handleEvent(event),这时 this 是这个对象,不是元素。这种写法适合把状态和处理逻辑放在一个对象里,解绑时也传同一个对象。
class 组件里想同时拿到实例和元素
在 class 里用箭头函数字段做回调,this 是实例,方便读写组件状态;需要元素就读 event.currentTarget。两件事分开拿,不要为了用 this 拿元素而放弃箭头函数,也不要为了拿实例去 bind 之后又忘了元素从哪来。
回答前,多想一步

容易答错的地方

把 target 当成挂监听器的元素
target 是事件最初发生的地方,可能是按钮里的文字、图标或者任何子元素。要拿挂监听器的元素,用 currentTarget。按钮里放了图标之后点击逻辑失灵,基本都是这个原因。
在异步代码里读 currentTarget
事件派发结束后浏览器会把 currentTarget 清空。回调里先 await 一个请求,再读 event.currentTarget,得到的是 null。需要的话在回调开头先存到变量里。
以为箭头函数回调里 this 也是元素
箭头函数的 this 在定义时就确定了,浏览器传进来的 this 被忽略。用了箭头函数又想拿元素,只能读 event.currentTarget。
试着用自己的话回答

面试官还会怎么问?

target 和 currentTarget 什么时候相等?

事件就发生在挂监听器的元素本身时,比如直接点在按钮上、而不是按钮里的子元素上,两者相等。冒泡或捕获经过的祖先元素上,两者就不同了。

removeEventListener 为什么有时候移不掉?

它按事件类型、回调函数引用和 capture 标志来匹配,三样都要一致。this 不参与匹配,但如果注册时传的是 handler.bind(this),解绑时再 bind 一次就是另一个函数,自然移不掉。要么把绑定后的函数存起来,要么用 AbortController 的 signal 统一移除。

Shadow DOM 里的事件,外面读到的 target 对吗?

事件冒泡出 Shadow DOM 边界时,target 会被重定向成宿主元素,外面看不到内部的具体节点。需要完整路径时用 event.composedPath(),前提是这个 shadow root 是 open 模式。

从一道题,走向一组知识

把知识连起来

函数与作用域

箭头函数的 this 指向谁?为什么 call 和 bind 改不了它?

箭头函数回调拿不到元素,根源就是箭头函数的 this 在定义时就定了,call 和 bind 也改不了。

函数与作用域

bind 和 call、apply 有什么区别?bind 返回的函数有什么特点?

回调里想让 this 指向组件实例时常用 bind,但每次 bind 都是新函数,解绑前要先存起来。

放回知识体系中复习JavaScript 进阶知识

参考资料

  • EventTarget: addEventListener() method

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

本题目录
  1. 先记住这个答案
  2. 事件委托里对比 this、target 和 currentTarget
  3. 为什么不同写法的 this 不一样
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

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

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