跳到主要内容
this、箭头函数与普通函数:前端实战避坑指南 | 极客日志
JavaScript 大前端
this、箭头函数与普通函数:前端实战避坑指南 综述由AI生成 JavaScript 中 this 绑定逻辑及箭头函数与普通函数的区别。核心结论是 this 由调用方式决定,箭头函数继承外层作用域 this。文章通过真实报错场景引入,对比两者在 this、arguments、构造函数能力上的差异。重点分析了后台项目中五大易错场景:UI 组件表格回调、Promise/async 异步回调、对象方法封装、事件监听器移除及数组方法回调。最后给出决策清单,指导开发者根据需求选择合适函数类型以避免常见坑点。
虚拟内存 发布于 2026/4/6 更新于 2026/5/26 48 浏览前言
无论你是刚学 JavaScript 的小白,还是已经写了几年代码的前端,只要在写后台管理系统,大概率都踩过 this 和箭头函数的坑。
这篇文章不讲特别玄学的底层原理,只回答三个问题:
日常写代码该怎么选? (普通函数 vs 箭头函数)
为什么这么选?
坑最容易出在哪里?
一、一个真实的报错场景
先看一段后台管理系统里常见的代码:
methods : {
handleDelete (id ) {
this .$confirm('确定删除吗?' ).then (() => {
this .deleteApi (id);
});
}
}
很多人会疑惑:我明明在 methods 里写的,this 怎么会是 undefined?
问题在于:this 不是由「你在哪写的」决定的,而是由「谁在调用这个函数」决定的。 而 $confirm().then() 里的回调,是 Promise 内部在调用,普通函数不会自动带上 Vue 实例的 this。
如果把 .then() 里的回调改成箭头函数,就不会报错了。后面会详细说明原因。
补充说明:回调函数概念
非回调函数:定义后由你直接调用(fn()),执行时机由你决定;
回调函数:把函数作为参数传递给另一个函数,由这个'接收方函数'在特定时机(比如异步操作完成、遍历完成)调用它。
function normalFn ( ) {
console .log ("我是普通函数,直接调用就执行" );
}
normalFn ();
function callbackFn ( ) {
console .log ("我是回调函数,由 forEach 调用" );
}
[ , , ]. (callbackFn);
[ , , ]. ( ( ) {
. ( , item);
});
( ( ) {
. ( );
}, );
1
2
3
forEach
1
2
3
forEach
function
item
console
log
"遍历到的元素:"
setTimeout
function
console
log
"1 秒后执行的回调函数"
1000
二、基础扫盲: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 通常是 window 或 undefined,而不是你组件里的 this。
三、箭头函数 vs 普通函数:本质区别 对比项 普通函数 箭头函数 this有属于自己的 this,由调用方式决定 没有自己的 this,使用外层作用域的 this arguments有 没有(可用 ...args 替代) 能否 new 可以 不可以 能否作为构造函数 可以 不可以
3.1. this 的区别(案例说明) 普通函数的 this 由调用方式决定,而箭头函数没有自己的 this,会'继承'外层作用域的 this。
const person = {
name : "张三" ,
sayNameNormal : function ( ) {
console .log ("普通函数 this:" , this .name );
},
sayNameArrow : () => {
console .log ("箭头函数 this:" , this .name );
}
};
person.sayNameNormal ();
person.sayNameArrow ();
const obj = {
num : 10 ,
fn : function ( ) {
setTimeout (function ( ) {
console .log ("普通嵌套函数 this.num:" , this .num );
}, 0 );
setTimeout (() => {
console .log ("箭头嵌套函数 this.num:" , this .num );
}, 0 );
}
};
obj.fn ();
普通函数 sayNameNormal 由 person 调用,this 就指向 person;
箭头函数 sayNameArrow 没有自己的 this,直接用外层(全局)的 this,而全局 this(window)没有 name 属性,所以是 undefined;
嵌套场景中,箭头函数能'捕获'外层函数的 this,这也是实际开发中箭头函数最常用的场景(避免手动绑定 this)。
3.2. arguments 的区别(案例说明) 普通函数有 arguments 对象(存储传入的所有参数),箭头函数没有,需用剩余参数 ...args 替代。
function normalFn ( ) {
console .log ("普通函数 arguments:" , arguments );
console .log ("第一个参数:" , arguments [0 ]);
}
const arrowFn = (...args ) => {
console .log ("箭头函数 args:" , args);
console .log ("第一个参数:" , args[0 ]);
};
normalFn (10 , 20 , 30 );
arrowFn (10 , 20 , 30 );
arguments 是类数组对象,只能在普通函数中使用;
箭头函数要获取所有参数,必须用 ES6 的剩余参数 ...args,args 是真正的数组,还能使用 map/filter 等数组方法,比 arguments 更灵活。
3.3. 能否 new / 作为构造函数(案例说明) 普通函数可以用 new 调用(作为构造函数),箭头函数不行,强行 new 会报错。
function Person (name ) {
this .name = name;
}
const p1 = new Person ("李四" );
console .log ("普通函数构造的实例:" , p1.name );
const ArrowPerson = (name ) => {
this .name = name;
};
try {
const p2 = new ArrowPerson ("王五" );
} catch (e) {
console .log ("箭头函数 new 报错:" , e.message );
}
this 指向:普通函数的 this 由调用方式决定,箭头函数继承外层作用域的 this(无自身 this);
参数获取:普通函数用 arguments,箭头函数无 arguments,需用 ...args 剩余参数;
构造函数能力:普通函数可 new 作为构造函数,箭头函数不行,强行 new 会报错。
这三个区别是箭头函数和普通函数最核心的差异,其中 this 指向的区别是实际开发中最常遇到、也最需要注意的点。
四、后台项目里最容易写错的 5 种场景
4.1 场景 1:Element UI / Ant Design 表格里的回调
<el-table-column label="操作" >
<template slot-scope ="scope" >
<el-button @click ="() => this.handleEdit(scope.row)" > 编辑</el-button >
</template >
</el-table-column>
<el-button @click ="handleEdit(scope.row)" > 编辑</el-button >
<el-button @click ="(row) => handleEdit(row)" > 编辑</el-button >
模板中的事件绑定,Vue 会自动将方法的 this 绑定到组件实例上(而非 window)。
错误写法:箭头函数的 this 固定指向全局上下文(window/undefined),this.handleEdit 会从 window 查找方法(找不到),且方法内部的 this 也会失效;
直接写方法名:Vue 会把模板里的 handleEdit 隐式映射到「组件实例的 methods」,并自动绑定组件的 this,是最安全高效的方式。
结论: 模板事件绑定优先直接写 方法名 (参数)(最优解);尽量不要用箭头函数包装(避免多余的函数创建 + 踩 this 坑);若因特殊场景非要用箭头函数,需去掉 this.(如 (row) => handleEdit(row)),但这种写法无必要,仅作为兜底参考。
4.2 场景 2:Promise / async 里的 this
handleSubmit ( ) {
this .validateForm ().then (function (res ) {
this .submitForm ();
});
}
handleSubmit ( ) {
this .validateForm ().then ((res ) => {
this .submitForm ();
});
}
原因: .then() 的回调是 Promise 内部调用的,普通函数不会自动绑定组件 this。用箭头函数可以继承 handleSubmit 所在作用域的 this,即组件实例。
结论: 在 Promise、async/await、setTimeout 等异步回调里,需要访问组件/外层 this 时,用箭头函数。
4.3 场景 3:对象方法 / API 封装
const api = {
baseUrl : '/api' ,
getList : () => {
return axios.get (this .baseUrl + '/list' );
}
};
const api = {
baseUrl : '/api' ,
getList ( ) {
return axios.get (this .baseUrl + '/list' );
}
};
**原因:**箭头函数没有自己的 this,会去外层找。这里的 getList 定义在对象字面量里,外层是全局,this 就是 window(或 undefined),自然拿不到 baseUrl。
结论: 对象方法、Class 方法需要用到 this 时,用普通函数,不要用箭头函数。
4.4 场景 4:事件监听器(addEventListener)
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 或普通函数,并把引用存到实例上,保证添加和移除用的是同一个函数。
4.5 场景 5:数组方法的回调(forEach、map、filter 等)
methods : {
processList ( ) {
const list = [1 , 2 , 3 ];
list.forEach (function (item ) {
this .doSomething (item);
});
list.forEach ((item ) => {
this .doSomething (item);
});
}
}
原因: forEach 等方法的回调是由数组方法内部调用的,普通函数不会绑定组件 this。用箭头函数可以继承 processList 的 this。
结论: 在 forEach、map、filter、reduce 等回调里需要访问外层 this 时,用箭头函数;不需要 this 时,两者都可以。
五、决策清单:什么时候用谁
对象方法、Class 方法、构造函数 → 用普通函数。
Promise、setTimeout、数组方法等回调里要访问外层 this → 用箭头函数。
Vue 模板事件 → 直接写方法名,或 (arg) => this.method(arg),避免乱包箭头函数。
需要 arguments → 用普通函数,或箭头函数 + ...args。
addEventListener / removeEventListener → 用 bind 或保存同一引用,保证添加和移除是同一个函数。
六、一句话口诀
普通函数 :有「自己的」this,谁调我,this 就指向谁。
箭头函数 :没有「自己的」this,用的是「定义时所在作用域」的 this。
因此,在需要「继承」外层 this 的场景(例如 Promise、setTimeout 回调),用箭头函数;在对象方法、构造函数等需要「自己的」this 的场景,用普通函数。需要「动态 this」用普通函数,需要「固定外层 this」用箭头函数。
总结 this 和箭头函数本身不复杂,容易出错的是「在错误场景选错写法」。后台项目里,最容易踩坑的就是:Promise 回调、对象方法、模板事件、事件监听器这几处。记住「谁在调用」「外层 this 是谁」,选普通函数还是箭头函数就不容易错。
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online