ES6 箭头函数:简洁语法与 this 绑定解析

68 阅读4分钟

在 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 代码。