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,此时 fn 和 user 没关系了 |
fn() |
直接调用,this 又变回 window |
💡 记忆口诀:
this 就看调用方式,不看定义位置。obj.fn() 就是 obj,fn() 就是 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 继承外层 window,window.age 不存在 |
introduce 里的 inner() |
undefined ❌ |
嵌套普通函数独立调用,this = window |
introduce 里的 innerArrow() |
小明 ✅ |
箭头函数继承外层 introduce 的 this(即 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 = this 或 let 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 永远不是这个对象。
⚠️ 新手易错:箭头函数没有自己的
this、arguments、super、new.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 |
setTimeout 里 this 丢失 |
回调改成箭头函数 | 箭头函数继承外层 this |
本章复盘
本章核心要点一览:
箭头函数与 this 核心要点:
| 要点 | 说明 |
|---|---|
| 箭头函数语法 | => 简写函数,单行可省略 {} 和 return,返回对象需 ({}) |
| this 的本质 | 谁调用就指向谁,运行时决定 |
| 普通函数 this | 取决于调用方式:obj.fn() → obj,fn() → window |
| 箭头函数 this | 继承外层作用域的 this,定义时就确定了 |
| 对象方法 | ❌ 不要用箭头函数,this 会指向错误 |
| 嵌套/回调函数 | ✅ 用箭头函数,自然继承外层 this |
| 构造函数 | ❌ 箭头函数不能 new |
| DOM 事件 | 需要 this 指向元素时用普通函数,否则箭头函数也行 |