1.1 箭头函数语法

箭头函数是 ES6 提供的一种简洁的函数写法,用 => 符号代替 function 关键字。

箭头函数让代码更短、更易读,但不能完全替代普通函数——两者的行为(尤其是 this)有本质区别。

语法速查表:
场景 写法 示例
多个参数 (参数1, 参数2) => 表达式 (a, b) => a + b
一个参数 参数 => 表达式 n => n * 2
没有参数 () => 表达式 () => 'Hi'
多行函数体 () => { ... } () => { let x = 1; return x; }
返回对象 () => ({ ... }) name => ({ name })
⚠️ 新手易错:返回一个对象时,必须用小括号包裹 {},否则大括号会被当成函数体。name => ({ name }) ✅,name => { name } ❌。
1// ========== 基础语法对比 ========== 2 3// 传统函数 4function add(a, b) { 5 return a + b; 6} 7 8// 箭头函数完整写法 9let add = (a, b) => { 10 return a + b; 11}; 12 13// 箭头函数简写(只有一行时省略 {} 和 return) 14let add = (a, b) => a + b; 15 16// 只有一个参数时,可以省略小括号 17let double = n => n * 2; 18 19// 没有参数时,必须写小括号 20let sayHi = () => 'Hi!';
代码逐行讲解:
代码 说明
function add(a, b) { ... } 传统函数写法
let add = (a, b) => { ... } 箭头函数完整写法,等价于传统函数
let add = (a, b) => a + b 只有一行表达式时省略 {}return
let double = n => n * 2 单参数省略小括号
let sayHi = () => 'Hi!' 无参数必须保留小括号
1// ========== 返回对象要特别注意 ========== 2// 错误写法:{} 会被当成函数体,不是对象 3// let getUser = (name) => { name: name }; // ❌ 报错 4 5// 正确写法:用小括号包起来 6let getUser = (name) => ({ name: name }); 7console.log(getUser('张三')); // { name: '张三' } 8 9// ========== 常见使用场景 ========== 10// 回调函数(配合数组方法) 11let numbers = [1, 2, 3, 4, 5]; 12 13// 传统写法 14let doubled1 = numbers.map(function(n) { 15 return n * 2; 16}); 17 18// 箭头函数写法(简洁很多) 19let doubled2 = numbers.map(n => n * 2); 20console.log(doubled2); // [2, 4, 6, 8, 10]

1.2 this是什么

this 就是"当前正在调用这个函数的对象"。通俗点说——谁调用了这个函数,this 就是谁

this 的值不是在写代码时确定的,而是在运行时根据"谁调用了它"来决定的。这是 JavaScript this 最容易让人迷糊的地方。

this 基本规则:
场景 this 指向谁
全局调用 fn() window(浏览器环境)
对象调用 obj.fn() obj
事件处理函数 触发事件的那个 DOM 元素
箭头函数 继承外层作用域的 this
1// ========== 情况1:全局调用 ========== 2function showThis() { 3 console.log(this); // window(浏览器中全局对象) 4} 5showThis(); // 等价于 window.showThis() 6 7// ========== 情况2:对象方法调用 ========== 8let user = { 9 name: '张三', 10 sayName: function() { 11 console.log(this); // user 对象本身 12 console.log(this.name); // 张三 13 } 14}; 15user.sayName(); // this = user(因为是 user 调用了 sayName) 16 17// ========== 情况3:普通函数赋值给变量再调用 ========== 18let fn = user.sayName; 19// fn(); // this 变成了 window,name 变成 undefined 20// 因为现在是 fn() 在调用,不是 user.fn() 在调用
代码逐行讲解:
代码 说明
showThis() 直接调用,this 指向 window(全局对象)
user.sayName() user 调用,this 指向 user
let fn = user.sayName 把函数拿出来赋给 fn,此时 fnuser 没关系了
fn() 直接调用,this 又变回 window
💡 记忆口诀this 就看调用方式,不看定义位置。obj.fn() 就是 objfn() 就是 window

1.3 普通函数vs箭头函数this

这是本章的核心重点——两者对 this 的处理方式完全不同。

普通函数的 this:运行时决定,谁调用就是谁。

箭头函数的 this:定义时决定,继承外层作用域的 this。

对比示例一:对象方法中(最关键的坑)

1let person = { 2 name: '小明', 3 age: 18, 4 5 // ✅ 普通函数:this 指向调用它的对象 6 sayName: function() { 7 console.log('普通函数:' + this.name); // 小明 8 }, 9 10 // ❌ 箭头函数:this 继承外层(这里是 window) 11 sayAge: () => { 12 console.log('箭头函数:' + this.age); // undefined(window.age 不存在) 13 }, 14 15 // 演示嵌套中的 this 差异 16 introduce: function() { 17 console.log('外层普通函数:' + this.name); // 小明 ✅ 18 19 // 嵌套普通函数(this 丢失) 20 function inner() { 21 console.log('内层普通函数:' + this.name); // undefined ❌ 22 } 23 24 // 嵌套箭头函数(this 继承外层) 25 let innerArrow = () => { 26 console.log('内层箭头函数:' + this.name); // 小明 ✅ 27 } 28 29 inner(); 30 innerArrow(); 31 } 32}; 33 34person.sayName(); // 普通函数:小明 35person.sayAge(); // 箭头函数:undefined 36person.introduce(); 37// 外层普通函数:小明 38// 内层普通函数:undefined 39// 内层箭头函数:小明
运行结果对比:
函数 结果 原因
person.sayName()(普通函数) 小明 person 调用,this = person
person.sayAge()(箭头函数) undefined 箭头函数 this 继承外层 windowwindow.age 不存在
introduce 里的 inner() undefined 嵌套普通函数独立调用,this = window
introduce 里的 innerArrow() 小明 箭头函数继承外层 introducethis(即 person

对比示例二:定时器中(经典场景)

1let team = { 2 name: '前端组', 3 members: ['小明', '小红', '小刚'], 4 5 // 传统写法:用 that = this 保存外层 this 6 showTeamOld: function() { 7 let that = this; // 先把 this 存起来 8 setTimeout(function() { 9 console.log('传统写法:' + that.name + '的成员'); 10 that.members.forEach(function(m) { 11 console.log(' - ' + m); 12 }); 13 }, 100); 14 }, 15 16 // 箭头函数写法:自然继承外层 this 17 showTeamNew: function() { 18 setTimeout(() => { 19 console.log('箭头写法:' + this.name + '的成员'); 20 this.members.forEach(m => { 21 console.log(' - ' + m); 22 }); 23 }, 100); 24 } 25}; 26 27team.showTeamOld(); 28// 传统写法:前端组的成员 29// - 小明 30// - 小红 31// - 小刚 32 33team.showTeamNew(); 34// 箭头写法:前端组的成员 35// - 小明 36// - 小红 37// - 小刚
💡 箭头函数出现之前,开发者用 let that = thislet self = this 保存外层 this,然后在内部函数里用 that。箭头函数让这种"存 this"的写法不再需要了。

1.4 适用场景

箭头函数并非万能,有些场景适合用,有些场景绝对不能用

适合用箭头函数的场景:
场景 示例 原因
数组方法的回调 arr.map(x => x * 2) 简洁,不需要自己的 this
定时器回调 setTimeout(() => {...}, 1000) 自然继承外层 this
Promise 回调 .then(data => ...) 简洁,不需要自己的 this
事件监听(不依赖 this) addEventListener('click', () => ...) 不需要 this 指向事件元素
不适合用箭头函数的场景:
场景 示例 原因
对象方法 { name: 'A', say: () => this.name } this 不指向对象本身
需要动态 this 的函数 button.onclick = () => this this 不是触发事件的元素
构造函数 let Person = () => {} 箭头函数不能 new
需要 arguments 的函数 () => console.log(arguments) 箭头函数没有 arguments
1// ✅ 适合:数组回调 2let articles = [ 3 { title: 'JavaScript入门', views: 100 }, 4 { title: 'CSS布局', views: 200 }, 5 { title: 'HTML语义化', views: 150 } 6]; 7 8// map 回调用箭头函数,简洁清晰 9let titles = articles.map(article => article.title); 10console.log(titles); // ['JavaScript入门', 'CSS布局', 'HTML语义化'] 11 12// filter 回调用箭头函数 13let popular = articles.filter(article => article.views > 120); 14console.log(popular); 15 16// ❌ 不适合:对象方法 17let badExample = { 18 name: '我是错误的', 19 say: () => { 20 // this 指向 window,不是 badExample 21 console.log(this.name); // undefined 22 } 23}; 24badExample.say(); 25 26// ✅ 对象方法用传统函数 27let goodExample = { 28 name: '我是正确的', 29 say() { // 方法简写,也是传统函数 30 console.log(this.name); // 我是正确的 31 } 32}; 33goodExample.say(); 34 35// ❌ 不适合:需要动态 this 的 DOM 事件 36let btn = document.createElement('button'); 37btn.innerText = '点我'; 38document.body.appendChild(btn); 39 40// 箭头函数里 this 是 window,不是按钮 41btn.addEventListener('click', () => { 42 console.log('箭头:', this); // window 43}); 44 45// 普通函数里 this 是触发事件的按钮元素 46btn.addEventListener('click', function() { 47 console.log('普通:', this); // <button>点我</button> 48});
选择速查表:
你要做什么 用什么函数
数组方法的回调函数 ✅ 箭头函数
对象的方法 ✅ 普通函数(或方法简写)
定时器/异步回调里要用外层 this ✅ 箭头函数
DOM 事件里要用 this 指向事件元素 ✅ 普通函数
需要 new 创建实例的构造函数 ✅ 普通函数
需要用 arguments 对象 ✅ 普通函数
⚠️ 新手易错对象的方法不要用箭头函数。这是最常见的坑。{ fn: () => this.xxx } 里的 this 永远不是这个对象。
⚠️ 新手易错:箭头函数没有自己的 thisargumentssupernew.target。它就是个"借用"外层 this 的简写函数,不是万能替代品。
⚠️ 新手易错:箭头函数不能用作构造函数new (() => {}) 会直接报错:TypeError: xxx is not a constructor

实践作业

题目:修复一段 this 指向错误的代码。

原始代码(下面的代码有3处 this 指向错误,请找出并修复):

1let blog = { 2 name: '我的博客', 3 posts: ['ES6入门', 'React教程', 'Next.js实战'], 4 5 // 错误1:对象方法用了箭头函数 6 showName: () => { 7 console.log('博客名:' + this.name); 8 }, 9 10 // 错误2:嵌套回调里的 this 丢失 11 showPosts: function() { 12 this.posts.forEach(function(post) { 13 console.log(this.name + ' - ' + post); 14 }); 15 }, 16 17 // 错误3:定时器里 this 丢失 18 delayedShow: function() { 19 setTimeout(function() { 20 console.log('延迟显示:' + this.name); 21 }, 1000); 22 } 23};

参考答案(先自己写,再对照):

1let blog = { 2 name: '我的博客', 3 posts: ['ES6入门', 'React教程', 'Next.js实战'], 4 5 // 修复1:对象方法改用普通函数 6 showName: function() { 7 console.log('博客名:' + this.name); // 博客名:我的博客 ✅ 8 }, 9 10 // 修复2:forEach 回调用箭头函数(继承外层 this) 11 showPosts: function() { 12 this.posts.forEach((post) => { 13 console.log(this.name + ' - ' + post); 14 // 我的博客 - ES6入门 15 // 我的博客 - React教程 16 // 我的博客 - Next.js实战 17 }); 18 }, 19 20 // 修复3:定时器回调用箭头函数(继承外层 this) 21 delayedShow: function() { 22 setTimeout(() => { 23 console.log('延迟显示:' + this.name); // 延迟显示:我的博客 ✅ 24 }, 1000); 25 } 26}; 27 28blog.showName(); 29blog.showPosts(); 30blog.delayedShow();
修复总结:
错误 修复方案 原因
对象方法用了箭头函数 改成 function() 对象方法需要 this 指向对象本身
forEach 回调里 this 丢失 回调改成箭头函数 箭头函数继承外层 this
setTimeoutthis 丢失 回调改成箭头函数 箭头函数继承外层 this

本章复盘

本章核心要点一览:

箭头函数与 this 核心要点:
要点 说明
箭头函数语法 => 简写函数,单行可省略 {}return,返回对象需 ({})
this 的本质 谁调用就指向谁,运行时决定
普通函数 this 取决于调用方式:obj.fn() → obj,fn() → window
箭头函数 this 继承外层作用域的 this,定义时就确定了
对象方法 ❌ 不要用箭头函数,this 会指向错误
嵌套/回调函数 ✅ 用箭头函数,自然继承外层 this
构造函数 ❌ 箭头函数不能 new
DOM 事件 需要 this 指向元素时用普通函数,否则箭头函数也行