skip to content
月与羽

箭头函数

/ 7 min read

是的,箭头函数(Arrow Functions)是 ES6 中引入的最重要的特性之一,它们与传统的 function 表达式相比,有几个非常特别且关键的区别。

它们绝不仅仅是“更短的函数语法”。核心区别在于它们如何处理 this 关键字,以及它们本身的一些限制。


1. 核心区别:this 的绑定方式(词法作用域 this

这是箭头函数最重要、最特别的地方。

  • 传统函数 (function): this 的值是在函数被调用时决定的。谁调用了它,this 就指向谁。这被称为“动态 this”。这常常会导致混乱。
  • 箭头函数 (=>): this 的值是在函数被定义时决定的。它会捕获其所在上下文(外层作用域)的 this 值作为自己的 this。这被称为“词法 this”(Lexical this)。箭头函数本身没有自己的 this

经典问题案例:setTimeout

让我们看看传统函数带来的问题:

const person_old = {
name: "Alice",
age: 30,
sayHelloAfterDelay: function() {
// 此时,this 指向 person_old 对象
console.log(`My name is ${this.name}.`); // My name is Alice.
setTimeout(function() {
// 问题来了!
// setTimeout 的回调函数是由 window 对象(或 Node.js 中的 Timeout 对象)调用的
// 所以这里的 this 指向了 window,而不是 person_old
console.log(`In 1 second, my name is ${this.name}.`); // In 1 second, my name is undefined. (在浏览器中)
}, 1000);
}
};
person_old.sayHelloAfterDelay();

ES5 的解决方案(很麻烦):通常用一个变量(如 thatself)来保存外部的 this

// ...
setTimeout(function() {
console.log(`My name is ${that.name}.`); // 手动保存 this
}, 1000);
// 或者使用 .bind(this)
setTimeout(function() {
console.log(`My name is ${this.name}.`);
}.bind(this), 1000); // 绑定 this

箭头函数的完美解决方案:

箭头函数天生就是为了解决这个问题而生的。

const person_new = {
name: "Bob",
age: 25,
sayHelloAfterDelay: function() {
// 此时,this 指向 person_new 对象
// 箭头函数没有自己的 this,它会“继承”外层作用域的 this
// 也就是 sayHelloAfterDelay 这个函数的 this,即 person_new 对象
setTimeout(() => {
console.log(`In 1 second, my name is ${this.name}.`); // In 1 second, my name is Bob.
}, 1000);
}
};
person_new.sayHelloAfterDelay();

这个特性使得在回调函数(如 setTimeout, map, forEach, 事件监听器)中使用箭头函数非常方便和可靠。


2. 更简洁的语法

这是最直观的区别,特别适用于回调和简单的单行函数。

  • 没有参数: () => ...

  • 只有一个参数: arg => ... (括号可以省略)

  • 多个参数: (arg1, arg2) => ... (括号不能省略)

  • 单行表达式(隐式返回): 如果函数体只有一行表达式,可以省略花括号 {}return 关键字。

    const numbers = [1, 2, 3];
    // 传统函数
    const squares_old = numbers.map(function(n) { return n * n; });
    // 箭头函数
    const squares_new = numbers.map(n => n * n); // 极其简洁
  • 多行函数体(显式返回): 如果函数体有多行语句,必须使用花括号 {},并且需要手动 return

    const sum = (a, b) => {
    console.log(`Adding ${a} and ${b}`);
    return a + b;
    };
  • 返回对象字面量:如果要隐式返回一个对象,必须用圆括号 () 包裹它,以避免与函数体的花括号 {} 混淆。

    // 错误!JS 引擎会认为 {} 是函数体的开始
    // const createUser_wrong = (name) => { name: name, age: 0 };
    // 正确!用 () 包裹对象字面量
    const createUser_right = (name) => ({ name: name, age: 0 });

3. 其他重要区别

a. 没有自己的 arguments 对象

  • 传统函数: 可以在函数内部访问 arguments 对象,它包含了所有传入的参数。
  • 箭头函数: 没有自己的 arguments 对象。如果在箭头函数中使用 arguments,它会引用外层(非箭头)函数的 arguments 对象。

现代解决方案: 使用剩余参数 (...args),它适用于所有类型的函数,并且是真正的数组。

// 传统函数
function logArgs() {
console.log(arguments); // [1, 2, 3] (类数组对象)
}
// 箭头函数
const logArgsArrow = (...args) => {
// console.log(arguments); // ReferenceError: arguments is not defined
console.log(args); // [1, 2, 3] (真正的数组)
};
logArgsArrow(1, 2, 3);

b. 不能作为构造函数(不能使用 new

  • 传统函数: 可以用作构造函数来创建对象实例。
  • 箭头函数: 绝对不能用 new 调用,否则会抛出 TypeError。因为它们没有内部的 [[Construct]] 方法,也没有 prototype 属性。这也和它们没有自己的 this 是一致的。
function Person(name) {
this.name = name;
}
const p = new Person("Alice"); // 可以
const Animal = (name) => {
this.name = name;
};
// const a = new Animal("Dog"); // TypeError: Animal is not a constructor

总结:何时使用/不使用箭头函数

功能/特性传统 function箭头函数 =>
this 绑定动态的 (由调用方式决定)词法的 (由定义位置决定)
arguments 对象有自己的 arguments没有,但可使用 ...rest
可否用 new可以 (作为构造函数)不可以
prototype 属性没有
适用场景对象的方法、构造函数、需要动态 this 的场景(如事件监听器中访问 this.target回调函数(如 map,filter,setTimeout)、需要绑定外层 this 的场景、任何不关心 this 的简单函数

经验法则:

  1. 当你需要一个会动态绑定 this 的函数时,比如定义一个对象的方法,或者在某些旧库的事件监听器中需要 this 指向触发事件的元素时,使用传统 function

    const myObj = {
    myMethod: function() { console.log(this); } // this 指向 myObj
    };
  2. 在所有其他地方,特别是回调函数中,优先使用箭头函数 =>。它能避免 this 相关的各种陷阱,并且语法更短,代码更清晰。