在 ES6 之前,我们常常需要在 JavaScript 代码里写大量的 function,并且还要小心处理 this 的指向问题。ES6 带来的箭头函数(Arrow Function)不仅让代码更简洁,还通过独特的 this 绑定机制,让我们彻底摆脱了 “this 到底指向谁” 的困惑。
作为一名长期写前端代码的开发者,我几乎在所有项目中都会使用箭头函数。从写回调,到数组方法,再到 React 组件中的事件绑定,它们不仅提高了我的编码效率,也让代码逻辑更加清晰。下面,我就从最常用的角度,总结箭头函数的写法、应用场景以及最关键的——this 指向规则。
一、箭头函数的基础语法
箭头函数的基本形式非常简单:
const fn = (arg) => {
// ...
};
如果只有一个参数,可以省略括号:
const double = x => x * 2
如果函数体只有一行并且是 return,可以省略花括号和 return:
const sum = (a, b) => a + b
如果没有参数:
const fn = () => console.log('hello')
当返回对象字面量时,需要用括号包裹对象:
const getUser = () => ({ name: 'Tom', age: 18 })
这类简洁写法,是箭头函数最吸引人的原因之一。
二、箭头函数适合用在哪些地方?
我在实际开发中,总结了几类最常使用箭头函数的场景。
1. 回调函数
例如在数组方法中:
numbers.map(n => n * 2)
避免 function(n) { … } 的冗余写法。
2. Promise 回调
fetch(url)
.then(res => res.json())
.catch(err => console.error(err))
链式结构更加紧凑。
3. React 事件与简易组件
const Button = () => <button onClick={() => alert('hi')}>Click</button>
4. 不需要自己的 this 的场景
比如工具函数、纯函数等。
三、重点:箭头函数中的 this 指向
箭头函数最关键的特性就是:它不会创建自己的 this。
传统函数中,this 的取值取决于调用方式:
const obj = {
num: 10,
fn: function() {
console.log(this.num) // 10
}
}
obj.fn()
但箭头函数完全不同 —— 它的 this 取决于它外层作用域的 this(词法作用域) ,也就是说,它的 this 在定义时就已经确定,而不是运行时。
1. 箭头函数的 this 是静态的
看一个常见例子:
const obj = {
num: 10,
fn: () => {
console.log(this.num)
}
}
obj.fn() // undefined
为什么是 undefined?
因为箭头函数的外层作用域是 全局作用域,不是 obj 对象。
在浏览器中,全局 this 是 window,而 window.num 不存在。
2. 在回调中非常好用
箭头函数常被用来避免手动绑定 this。
例如定时器:
function Person() {
this.age = 10;
setInterval(() => {
this.age++;
console.log(this.age);
}, 1000);
}
new Person()
如果不使用箭头函数,内部 this 会丢失,需要 .bind(this) 或 const that = this。
但因为箭头函数不创建自己的 this,它会继承外层 Person 的 this,因此不用额外处理。
3. 不能用在需要动态 this 的地方
因为箭头函数没有 this,所以也不能用来当构造函数:
const Person = () => {}
new Person() // 报错:Person is not a constructor
也不能使用箭头函数作为对象方法需要 this 的场景:
const obj = {
a: 1,
getA: () => this.a
}
obj.getA() // undefined
4.举列
你看到了这个例子:
const person = {
name: "小明",
hobbies: ["篮球", "游泳", "唱歌"],
showHobbies: function() {
this.hobbies.forEach(function(hobby) { //这里this指向person
// 这里的this指向全局window
console.log(this.name + "喜欢" + hobby); // 输出:undefined喜欢篮球
});
}
};
person.showHobbies(); // 结果不对!
你可能会想:
- 回调函数写在 person 里面
- 外层作用域不是 person 吗?
- 那第二个的 this 为什么不是 person?
但 JavaScript 的规则不是“写在谁里面”,而是:
普通函数的 this 只看调用方式。
例子中的回调函数是 forEach 内部调用的,而不是 person 调用的。
本质调用方式类似这样:
function tmp(hobby) { ... }
tmp(hobby); // 普通函数调用,没有调用者
因此:
- 没有被某个对象调用
- this 默认指向 window(严格模式下是 undefined)
所以输出:
undefined喜欢篮球
对比一下箭头函数版本:
showHobbies() {
this.hobbies.forEach(hobby => {
console.log(this.name + "喜欢" + hobby);
});
}
这里的 this 指向 person
因为箭头函数:
- 不会创建自己的 this
- 会继承外层函数 showHobbies 的 this
- 而 showHobbies 是通过 person 调用的(person.showHobbies())
- 所以箭头函数的 this = person
简单图示:
普通函数:
person --(调用)--> showHobbies() this = person
|
| forEach 回调以普通函数方式调用
↓
function() this = window
箭头函数:
person --(调用)--> showHobbies() this = person
|
|
↓
=> 回调继承外层 this → person
四、箭头函数不能用的几个场景
为了避免踩坑,有几个地方我在写代码时一定不会使用箭头函数:
1. 对象方法(需要 this)
const obj = {
a: 1,
getA: () => this.a
}
箭头函数拿不到 obj 的 this。
2. 需要 arguments 的地方
箭头函数没有 arguments:
const fn = () => console.log(arguments) // 报错
必须用 ...rest 才能实现:
const fn = (...args) => console.log(args)
3. 构造函数
箭头函数不能用 new。
4. 原型方法
原型方法大多需要动态 this,因此用普通函数更合适。
五、总结
-
普通函数 function() 的 this 与写在哪里无关
-
普通函数 function() 的 this 只看是谁调用它
-
箭头函数 的 this:看“在哪定义”
掌握箭头函数及其 this 绑定机制,将帮助你编写更清晰、更可靠的 JavaScript 代码。