《this、箭头函数与普通函数:后台项目里最容易写错的几种场景》

前言

无论你是刚学 JavaScript 的小白,还是已经写了几年代码的前端,只要在写后台管理系统,大概率都踩过 this 和箭头函数的坑。

这篇文章不讲特别玄学的底层原理,只回答三个问题:

  1. 日常写代码该怎么选?(普通函数 vs 箭头函数)
  2. 为什么这么选?
  3. 坑最容易出在哪里?

一、一个真实的报错场景

先看一段后台管理系统里常见的代码:

javascript 复制代码
// 表格操作列有个「删除」按钮
methods: {
  handleDelete(id) {
    this.$confirm('确定删除吗?').then(() => {
      this.deleteApi(id);  // ❌ 报错:Cannot read property 'deleteApi' of undefined
    });
  }
}

很多人会疑惑:我明明在 methods 里写的,this 怎么会是 undefined

问题在于:this 不是由「你在哪写的」决定的,而是由「谁在调用这个函数」决定的。$confirm().then() 里的回调,是 Promise 内部在调用,普通函数不会自动带上 Vue 实例的 this

如果把 .then() 里的回调改成箭头函数,就不会报错了。后面会详细说明原因。

二、基础扫盲:this 到底是谁决定的

核心结论:this 由「调用方式」决定,而不是由「定义位置」决定。

调用方式 this 指向 典型场景
作为对象方法调用 该对象 obj.fn() → this 是 obj
直接调用 fn() 严格模式:undefined;非严格:window 孤立的函数调用
new 调用 新创建的对象 new Foo()
call/apply/bind 传入的第一个参数 显式指定 this
作为回调传入 谁调就指向谁,通常丢 this setTimeout(fn)、Promise.then(fn)

关键点:当函数被当作回调传给别人时,谁调这个函数,this 就由谁决定。 比如 setTimeout(fn) 里,是浏览器在调 fn,所以 this 通常是 windowundefined,而不是你组件里的 this

三、箭头函数 vs 普通函数:本质区别

对比项 普通函数 箭头函数
this 有属于自己的 this,由调用方式决定 没有自己的 this,使用外层作用域的 this
arguments 没有(可用 ...args 替代)
能否 new 可以 不可以
能否作为构造函数 可以 不可以

一句话区分:

  • 普通函数:有「自己的」this,谁调我,this 就指向谁。
  • 箭头函数:没有「自己的」this,用的是「定义时所在作用域」的 this

因此,在需要「继承」外层 this 的场景(例如 PromisesetTimeout 回调),用箭头函数;在对象方法、构造函数等需要「自己的」this 的场景,用普通函数。

四、后台项目里最容易写错的 5 种场景

场景 1:Element UI / Ant Design 表格里的回调

javascript 复制代码
// ❌ 错误写法:在模板里用箭头函数包装,可能拿不到正确的 this
<el-table-column label="操作">
  <template slot-scope="scope">
    <el-button @click="() => this.handleEdit(scope.row)">编辑</el-button>
  </template>
</el-table-column>

// ✅ 正确写法:直接传方法引用,Vue 会帮你绑定 this
<el-button @click="handleEdit(scope.row)">编辑</el-button>

原因: 模板里的事件绑定,Vue 会自动把组件的 this 绑定到方法上。用箭头函数包装后,this 会在定义时就固定,可能指向 windowundefined,反而拿不到组件实例。

结论: 模板事件尽量直接写方法名,或写 (arg) => this.method(arg),不要在模板里随便包箭头函数。

场景 2:Promise / async 里的 this

javascript 复制代码
// ❌ 错误:.then 里用普通函数,this 丢失
handleSubmit() {
  this.validateForm().then(function(res) {
    this.submitForm();  // this 是 undefined!
  });
}

// ✅ 正确:用箭头函数,继承外层的 this
handleSubmit() {
  this.validateForm().then((res) => {
    this.submitForm();  // this 正确指向组件实例
  });
}

原因: .then() 的回调是 Promise 内部调用的,普通函数不会自动绑定组件 this。用箭头函数可以继承 handleSubmit 所在作用域的 this,即组件实例。

结论:Promiseasync/awaitsetTimeout 等异步回调里,需要访问组件/外层 this 时,用箭头函数。

场景 3:对象方法 / API 封装

javascript 复制代码
// ❌ 错误:箭头函数作为对象方法,this 指向外层(window)
const api = {
  baseUrl: '/api',
  getList: () => {
    return axios.get(this.baseUrl + '/list');  // this.baseUrl 是 undefined!
  }
};

// ✅ 正确:用普通函数
const api = {
  baseUrl: '/api',
  getList() {
    return axios.get(this.baseUrl + '/list');
  }
};

原因: 箭头函数没有自己的 this,会去外层找。这里的 getList 定义在对象字面量里,外层是全局,this 就是 window(或 undefined),自然拿不到 baseUrl

结论: 对象方法、Class 方法需要用到 this 时,用普通函数,不要用箭头函数。

场景 4:事件监听器(addEventListener)

javascript 复制代码
// 场景:监听 window 滚动,组件销毁时需要移除监听

// ❌ 错误:箭头函数每次都是新引用,无法正确 removeEventListener
mounted() {
  window.addEventListener('scroll', () => this.handleScroll());
},
beforeDestroy() {
  window.removeEventListener('scroll', () => this.handleScroll());  // 移除失败!引用不同
}

// ✅ 正确:保存同一个函数引用
mounted() {
  this.boundHandleScroll = this.handleScroll.bind(this);
  window.addEventListener('scroll', this.boundHandleScroll);
},
beforeDestroy() {
  window.removeEventListener('scroll', this.boundHandleScroll);
}

原因: removeEventListener 必须传入和 addEventListener 时完全相同的函数引用。每次写 () => this.handleScroll() 都会生成新函数,所以无法正确移除。

结论: 需要手动移除监听时,用 bind 或普通函数,并把引用存到实例上,保证添加和移除用的是同一个函数。

场景 5:数组方法的回调(forEachmapfilter 等)

javascript 复制代码
// 在 Vue 组件里
methods: {
  processList() {
    const list = [1, 2, 3];
    
    // ❌ 错误:普通函数作为 forEach 回调,this 会丢
    list.forEach(function(item) {
      this.doSomething(item);  // this 是 undefined
    });
    
    // ✅ 正确:箭头函数继承外层的 this
    list.forEach((item) => {
      this.doSomething(item);
    });
  }
}

原因: forEach 等方法的回调是由数组方法内部调用的,普通函数不会绑定组件 this。用箭头函数可以继承 processListthis

结论:forEachmapfilterreduce 等回调里需要访问外层 this 时,用箭头函数;不需要 this 时,两者都可以。

五、决策清单:什么时候用谁

可以按下面几条来选:

  1. 对象方法、Class 方法、构造函数 → 用普通函数。
  2. Promise、setTimeout、数组方法等回调里要访问外层 this → 用箭头函数。
  3. Vue 模板事件 → 直接写方法名,或 (arg) => this.method(arg),避免乱包箭头函数。
  4. 需要 arguments → 用普通函数,或箭头函数 + ...args
  5. addEventListener / removeEventListener → 用 bind 或保存同一引用,保证添加和移除是同一个函数。

六、一句话口诀

  • 普通函数 :有自己的 this,谁调我,this 就指向谁。
  • 箭头函数 :没有自己的 this,用的是「定义时所在外层」的 this

需要「动态 this」用普通函数,需要「固定外层 this」用箭头函数。

总结

this 和箭头函数本身不复杂,容易出错的是「在错误场景选错写法」。后台项目里,最容易踩坑的就是:Promise 回调、对象方法、模板事件、事件监听器这几处。记住「谁在调用」「外层 this 是谁」,选普通函数还是箭头函数就不容易错。

以上就是本次的学习分享,欢迎大家在评论区讨论指正,与大家共勉。

我是 Eugene,你的电子学友。

如果文章对你有帮助,别忘了点赞、收藏、加关注,你的认可是我持续输出的最大动力~

相关推荐
yma162 小时前
前端react模拟内存溢出——chrome devtool查找未释放内存
前端·chrome·react.js
colicode2 小时前
Objective-C语音验证码接口API示例代码:老版iOS应用接入语音验证教程
前端·c++·ios·前端框架·objective-c
小圣贤君2 小时前
从「脑内人设」到「一眼入魂」:51mazi 小说人物图 AI 生成实战
前端·人工智能·文生图·ai写作·通义万相·写作软件·小说人物
Jing_Rainbow2 小时前
【React-11/Lesson95(2026-01-04)】React 闭包陷阱详解🎯
前端·javascript·react.js
麦芽糖02192 小时前
微信小程序七-2 npm包以及全局数据共享
前端·小程序·npm
Zhencode2 小时前
深入Vue3响应式核心:computed 的实现原理与应用
前端·javascript·vue.js
独自破碎E3 小时前
【滑动窗口】BISHI47 交换到最大
java·开发语言·javascript
剑亦未配妥3 小时前
CSS 折叠引发的 scrollHeight 异常 —— 一次 Blink 引擎的诡异 Bug
前端·css·bug