this、箭头函数与普通函数:前端实战避坑指南 | JS 基础语法与数据操作篇

this、箭头函数与普通函数:前端实战避坑指南 | JS 基础语法与数据操作篇
【this/箭头函数/普通函数】+【前端中后台开发】:从【this绑定逻辑】到【落地实操】,彻底搞懂【函数选型】的最佳写法,避开Promise/模板事件/对象方法高频坑!
在这里插入图片描述

📑 文章目录


同学们好,我是 Eugene(尤金),一个拥有多年中后台开发经验的前端工程师~

(Eugene 发音很简单,/juːˈdʒiːn/,大家怎么顺口怎么叫就好)

你是否也有过:明明学过很多技术,一到关键时候却讲不出来、甚至写不出来?

你是否也曾怀疑自己,是不是太笨了,明明感觉会,却总差一口气?

就算想沉下心从头梳理,可工作那么忙,回家还要陪伴家人。

一天只有24小时,时间永远不够用,常常感到力不从心。

技术行业,本就是逆水行舟,不进则退。

如果你也有同样的困扰,别慌。

从现在开始,跟着我一起心态归零,利用碎片时间,来一次彻彻底底的基础扫盲

这一次,我们一起慢慢来,扎扎实实变强。

不搞花里胡哨的理论堆砌,只分享看得懂、用得上的前端干货,

咱们一起稳步积累,真正摆脱“面向搜索引擎写代码”的尴尬。


前言

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

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

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

⬆ 返回目录


一、一个真实的报错场景

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

// 表格操作列有个「删除」按钮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() 里的回调改成箭头函数,就不会报错了。后面会详细说明原因。

ps·补充回调函数概念(供新手同学参考)
  • 非回调函数:定义后由你直接调用(fn()),执行时机由你决定;
  • 回调函数:把函数作为参数传递给另一个函数,由这个 “接收方函数” 在特定时机(比如异步操作完成、遍历完成)调用它。
// 1. 普通函数:自己定义、自己调用functionnormalFn(){ console.log("我是普通函数,直接调用就执行");}// 主动调用普通函数normalFn();// 输出:我是普通函数,直接调用就执行// 2. 回调函数:作为参数传递,由其他函数调用functioncallbackFn(){ console.log("我是回调函数,由forEach调用");}// forEach是接收回调的主函数,它会遍历数组时调用回调函数[1,2,3].forEach(callbackFn);// 输出3次:我是回调函数,由forEach调用// 更常见的写法:匿名回调函数(简化版)[1,2,3].forEach(function(item){ console.log("遍历到的元素:", item);// 异步/同步场景都常用});// 异步回调(典型场景:定时器)setTimeout(function(){ console.log("1秒后执行的回调函数");// 1秒后才执行,时机由setTimeout决定},1000);

⬆ 返回目录


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

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

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

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

⬆ 返回目录


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

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

⬆ 返回目录

3.1. this 的区别(案例说明)

普通函数的 this 由调用方式决定,而箭头函数没有自己的 this,会 “继承” 外层作用域的 this

// 案例1:对象方法中的 thisconst person ={name:"张三",// 普通函数作为对象方法sayNameNormal:function(){ console.log("普通函数 this:",this.name);// this 指向调用者 person},// 箭头函数作为对象方法sayNameArrow:()=>{ console.log("箭头函数 this:",this.name);// this 指向外层(全局window),没有name属性}}; person.sayNameNormal();// 输出:普通函数 this: 张三 person.sayNameArrow();// 输出:箭头函数 this: undefined// 案例2:嵌套函数中的 thisconst obj ={num:10,fn:function(){// 普通嵌套函数:this 指向全局(非严格模式)setTimeout(function(){ console.log("普通嵌套函数 this.num:",this.num);// undefined},0);// 箭头嵌套函数:this 继承外层 fn 的 this(即obj)setTimeout(()=>{ console.log("箭头嵌套函数 this.num:",this.num);// 10},0);}}; obj.fn();
解释:
  • 普通函数 sayNameNormalperson 调用,this 就指向 person
  • 箭头函数 sayNameArrow 没有自己的 this,直接用外层(全局)的 this,而全局 thiswindow)没有 name 属性,所以是 undefined
  • 嵌套场景中,箭头函数能 “捕获” 外层函数的 this,这也是实际开发中箭头函数最常用的场景(避免手动绑定 this)。

⬆ 返回目录

3.2. arguments 的区别(案例说明)

普通函数有 arguments 对象(存储传入的所有参数),箭头函数没有,需用剩余参数 ...args 替代。

// 普通函数:有 argumentsfunctionnormalFn(){ console.log("普通函数 arguments:", arguments);// 输出传入的参数集合 console.log("第一个参数:", arguments[0]);}// 箭头函数:无 arguments,用 ...args 替代constarrowFn=(...args)=>{// console.log(arguments); // 直接用会报错:ReferenceError: arguments is not defined console.log("箭头函数 args:", args);// 数组形式存储参数 console.log("第一个参数:", args[0]);};normalFn(10,20,30);// 输出:普通函数 arguments: [Arguments] { '0': 10, '1': 20, '2': 30 }// 输出:第一个参数: 10arrowFn(10,20,30);// 输出:箭头函数 args: [ 10, 20, 30 ]// 输出:第一个参数: 10
解释:
  • arguments 是类数组对象,只能在普通函数中使用;
  • 箭头函数要获取所有参数,必须用 ES6 的剩余参数 ...argsargs 是真正的数组,还能使用 map/filter 等数组方法,比 arguments 更灵活。

⬆ 返回目录

3.3. 能否 new / 作为构造函数(案例说明)

普通函数可以用 new 调用(作为构造函数),箭头函数不行,强行 new 会报错。

// 普通函数:可作为构造函数functionPerson(name){this.name = name;}const p1 =newPerson("李四"); console.log("普通函数构造的实例:", p1.name);// 李四// 箭头函数:不能作为构造函数constArrowPerson=(name)=>{this.name = name;};try{const p2 =newArrowPerson("王五");// 强行new会报错}catch(e){ console.log("箭头函数 new 报错:", e.message);// ArrowPerson is not a constructor}
解释:
  • 构造函数的核心是 new 操作会创建新对象,并把函数的 this 绑定到这个新对象;
  • 箭头函数没有自己的 this,也没有 prototype 属性(构造函数的必要条件),所以无法作为构造函数使用。
小结:
  1. this 指向:普通函数的 this 由调用方式决定,箭头函数继承外层作用域的 this(无自身 this);
  2. 参数获取:普通函数用 arguments,箭头函数无 arguments,需用 ...args 剩余参数;
  3. 构造函数能力:普通函数可 new 作为构造函数,箭头函数不行,强行 new 会报错。

这三个区别是箭头函数和普通函数最核心的差异,其中 this 指向的区别是实际开发中最常遇到、也最需要注意的点。

⬆ 返回目录


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

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

 // ❌ 错误写法:箭头函数里加 this.,会从 window 找方法,导致 this 失效 <el-table-columnlabel="操作"><templateslot-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> // ⚠️ 能运行但不推荐:箭头函数里不加 this.(Vue 会映射到组件方法),但多一层包装无意义 <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

// ❌ 错误:.then 里用普通函数,this 丢失handleSubmit(){this.validateForm().then(function(res){this.submitForm();// this 是 undefined!});}// ✅ 正确:用箭头函数,继承外层的 thishandleSubmit(){this.validateForm().then((res)=>{this.submitForm();// this 正确指向组件实例});}

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

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

⬆ 返回目录


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

// ❌ 错误:箭头函数作为对象方法,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.4 场景 4:事件监听器(addEventListener)

// 场景:监听 window 滚动,组件销毁时需要移除监听// ❌ 错误:箭头函数每次都是新引用,无法正确 removeEventListenermounted(){ 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:数组方法的回调(forEachmapfilter 等)

// 在 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 的场景(例如 PromisesetTimeout 回调),用箭头函数;在对象方法、构造函数等需要「自己的」this 的场景,用普通函数。需要「动态 this」用普通函数,需要「固定外层 this」用箭头函数。

⬆ 返回目录


总结

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

⬆ 返回目录

🔍 系列模块导航

📝 JS 基础语法与数据操作

一、《var/let/const:变量与作用域实战选型|JS 基础语法与数据操作篇》
二、《this、箭头函数与普通函数:前端实战避坑指南 | JS 基础语法与数据操作篇》
三、《对象解构赋值:接口数据解包 10 个实战写法|JS 基础语法与数据操作篇》
四、《map/filter/reduce:数组10个常用实战操作|JS 基础语法与数据操作篇》
五、《find/some/every/includes:数组查找与判断实战用法|JS 基础语法与数据操作篇》
六、《sort/localeCompare:对象数组排序与分组实战|JS 基础语法与数据操作篇》
七、《模板字符串 /split/join/ 正则:字符串处理实战|JS 基础语法与数据操作篇》
八、《Date/dayjs:日期时间处理实战|JS 基础语法与数据操作篇》
九、《try/catch/Promise:前端错误处理实战|JS 基础语法与数据操作篇》
十、《import/export:前端模块化实战|JS 基础语法与数据操作篇》

👉 跟着系列慢慢学,把技术功底扎扎实实地打牢~

📚 系列总览

前端体系化学习完全体:基础 → 规范 → 架构 → 大厂面试
四套系列、百余篇高质量实战文,从入门到进阶,一站式补齐前端核心能力
  • 前端基础实战系列《前端基础实战:JS/TS与Vue体系化扫盲(47 篇完整目录 + 避坑)》
  • 前端规范实战系列:工程化编码规范 + API / 交互 / 组件统一标准(持续更新中)
  • 前端架构实战系列:聚焦工程化、性能优化、可维护架构、中后台体系设计(规划中)
  • 前端大厂面试系列:覆盖高频考点、手写题、项目深挖、简历与面试技巧(规划中)

每个系列完结后,都会整理成一篇完整导航文并附上直达链接,方便大家按顺序、体系化学习。

全套内容持续更新中,敬请期待~

⬆ 返回目录


学习本就是一场持久战,不需要急着一口吃成胖子。哪怕今天你只记住了一点点,这都是实打实的进步。

后续我还会继续用这种大白话、讲实战方式,带大家扫盲更多前端基础。

关注我,不迷路,咱们把那些曾经模糊的知识点,一个个彻底搞清楚。

如果你觉得这篇内容对你有帮助,不妨点赞+收藏,下次写代码卡壳时,拿出来翻一翻,比搜引擎更靠谱。

我是 Eugene,你的电子学友,我们下一篇干货见~

Read more

从 OpenClaw 到 ToClaw:AI 代理网关的产品化之路

从 OpenClaw 到 ToClaw:AI 代理网关的产品化之路

定位说明:这是一篇偏“体验与选型思路”的横测笔记,不是参数党跑分,也不是安装教程。内容基于我对产品定位与常见使用路径的理解,公测策略与功能细节可能会随版本变化。 01|OpenClaw 是什么?能做什么? OpenClaw 可以理解为一种“AI 代理(Agent)网关/中枢”:你在聊天界面下指令,它会调用模型能力并配合工具,去做更接近“完成任务”的事情,而不是只聊天。它强调可扩展(技能/插件)、可接入多渠道、可在你自己的设备上运行等方向。 你能用 OpenClaw 做什么(偏通用能力) * 在聊天软件里接收任务、输出结果,并尽量保持持续记忆与上下文(取决于你的配置与使用方式) * 通过工具/技能扩展能力:文件读写、浏览器自动化、系统命令、定时任务、接入第三方服务等(不同发行与生态会有差异) 但现实门槛也很明显 * 自部署往往需要 Node.js

Llama-2-7b-Chat-hf模型安装与使用指南

Llama-2-7b-Chat-hf模型安装与使用指南 【免费下载链接】Llama-2-7b-chat-hf 项目地址: https://ai.gitcode.com/hf_mirrors/NousResearch/Llama-2-7b-chat-hf 概述 Llama-2-7b-Chat-hf是Meta开发的70亿参数对话优化大语言模型,采用先进的Transformer架构,专门针对对话场景进行了精细调优。该模型在Hugging Face Transformers格式下提供了完整的预训练权重和分词器配置,支持多种自然语言生成任务。 系统要求 硬件配置 * 内存要求:至少16GB RAM(推荐32GB以上) * 存储空间:模型文件约13GB,需预留充足空间 * GPU支持:推荐使用NVIDIA GPU,CUDA 11.0+版本 软件环境 * 操作系统:Linux、Windows或macOS * Python版本:3.7及以上 * 深度学习框架:PyTorch 1.8.0+ * 必备库:transformers、

Stable Diffusion 3.5 开发指南(三):Stable Diffusion 3.5 LoRA 微调

概述 在之前的章节中,我们学习了如何获取和调用 Stable Diffusion 3.5 模型,以及深入理解了其核心的 Flow Matching 机制。本章将聚焦于LoRA(Low-Rank Adaptation)微调技术,这是一种高效的模型定制方法,能够在保持原有模型性能的同时,仅通过少量参数更新即可实现特定任务的定制化。 1. 数据集准备 1.1 数据集格式 微调 Stable Diffusion 3.5 模型需要图像-文本对数据集,每个数据项应包含以下两个核心字段: * img_path:图像文件的路径(支持绝对路径或相对路径) * caption:与图像内容精准匹配的文本描述 示例 JSON 数据集格式 [{"img_path":"/path/to/image1.jpg"

技术创作者的新防线:如何在AIGC时代守护你的数字作品价值?

当“智能生成”成为一把双刃剑 清晨八点,一位全栈工程师在个人技术博客上发布了昨晚精心撰写的《React性能优化十则》。三小时后,他却在另一个技术社区看到了几乎相同的文章,署名却是一个陌生的账号。更让他困惑的是,文章的核心代码片段和结构高度相似,但部分解释性文字明显带有AI生成的特征。他面临一个现代创作者最无奈的困境:我的原创内容,是被洗稿了,还是被AI“学习”后重组了? 这不是虚构的场景。在ZEEKLOG、GitHub、知乎等技术社区,类似的事件正以几何级数增长。AIGC工具的普及,一方面极大提升了内容生产效率,另一方面却让原创性验证、内容真实性、代码合规性成为了技术创作者必须面对的新挑战。 技术创作者的三大核心痛点与场景解析 场景一:技术博客与开源文档——如何守护你的“知识主权”? 作为ZEEKLOG的核心用户群体,技术博主和开源贡献者每天生产着海量的教程、解决方案和项目文档。痛点显而易见: 1. 文章被AI洗稿与重组:你发布的原创技术方案,可能被他人用AI工具改写核心段落后,冠以新的标题重新发布。 2. 代码示例的“隐性抄袭”:你精心编写的、解决特定问题的代码片段