箭头函数是 ES6(ES2015)引入的一种新的函数定义方式。它不仅仅是写法上的简化,更在 this 绑定、arguments 对象等核心行为上与普通函数存在本质区别。理解这些差异,是写出可预测、无 Bug 的 JavaScript 代码的关键。
9.5.1 箭头函数的基本语法
箭头函数的语法非常简洁,根据参数数量和函数体复杂度,可以有不同的简写形式:
// 无参数:必须写一对空括号
const sayHello = () => console.log('Hello');
// 单个参数:可以省略括号
const double = x => x * 2;
// 多个参数:必须用括号包裹
const add = (a, b) => a + b;
// 函数体多行:需要用大括号,并显式 return
const sum = (a, b) => {
const result = a + b;
return result;
};
// 直接返回对象字面量:需要用小括号包裹对象
const createUser = (name, age) => ({ name, age });
箭头函数让匿名回调的书写变得极为清爽,尤其在数组操作中:
const numbers = [1, 2, 3, 4];
const doubled = numbers.map(n => n * 2); // [2, 4, 6, 8]
const evens = numbers.filter(n => n % 2 === 0); // [2, 4]
9.5.2 与普通函数的核心差异
虽然箭头函数看起来只是普通函数的“简写版”,但它在四个关键行为上有根本不同。
差异一:没有自己的 this
普通函数的 this 是动态绑定的,取决于调用方式。箭头函数没有自己的 this,它会捕获定义时所在上下文的 this 值,这个值在整个生命周期中不会改变。这就是所谓的“词法 this”。
// 普通函数:this 取决于调用方式
const obj1 = {
name: 'Alice',
greet: function() {
console.log(this.name);
}
};
obj1.greet(); // 'Alice',this 指向 obj1
const greetFn = obj1.greet;
greetFn(); // undefined(非严格模式下指向 window,严格模式下 undefined)
// 箭头函数:this 继承自定义时的外层作用域
const obj2 = {
name: 'Bob',
greet: () => {
console.log(this.name);
}
};
obj2.greet(); // undefined,因为定义时外层 this 不是 obj2(可能是 window/global)
典型应用场景:在回调函数中保留外层的 this。
class Timer {
constructor() {
this.seconds = 0;
// 普通函数需要 bind,箭头函数直接使用外层 this
setInterval(() => {
this.seconds++; // this 指向 Timer 实例
}, 1000);
}
}
使用普通函数时,setInterval(function(){...}) 中的 this 会丢失指向,而箭头函数天然绑定定义时的 this,这也是它在前端框架(如 React 类组件)中广受欢迎的原因。
差异二:没有 arguments 对象
普通函数内部可以使用 arguments 对象访问所有传入的实参。箭头函数没有自己的 arguments,如果在其中使用 arguments,会引用外层作用域的 arguments,这通常不是期望的行为。
function outer() {
// 普通函数
const normal = function() {
console.log(arguments);
};
normal(1, 2, 3); // [1, 2, 3]
// 箭头函数
const arrow = () => {
console.log(arguments);
};
arrow(4, 5, 6); // 可能输出 outer 函数的 arguments
}
outer('a', 'b');
正确做法:使用剩余参数(rest parameter)代替。
const arrow = (...args) => {
console.log(args); // [4, 5, 6]
};
arrow(4, 5, 6);
差异三:不能作为构造函数
箭头函数没有 [[Construct]] 内部方法,不能使用 new 调用。尝试对箭头函数使用 new 会抛出 TypeError。
const Person = (name) => {
this.name = name; // 这里的 this 是外层作用域的
};
// const p = new Person('Jack'); // 报错:Person is not a constructor
同时,箭头函数也没有 prototype 属性:
const fn = () => {};
console.log(fn.prototype); // undefined
这一特性决定了,如果需要创建可实例化的对象构造器,必须使用普通函数或 class。
差异四:不能使用 yield
箭头函数不能用作生成器函数,即不能在函数体内使用 yield 关键字。如果需要定义生成器,必须使用 function* 语法。
// 普通生成器
function* gen() {
yield 1;
}
// 箭头函数无法定义生成器
// const gen = *() => {}; // 语法错误
9.5.3 适用场景与避坑指南
推荐使用箭头函数的场景:
- 简短的回调函数,尤其是数组方法、Promise 链、事件监听器需要绑定外层
this时。 - 函数体内不需要独立
this、arguments,且不希望被动态this所困扰的场合。
不推荐或应避免的场景:
- 对象方法(尤其是需要访问当前对象的属性时),除非你明确想要绑定外层
this。 - 需要使用
arguments对象时(用剩余参数替代)。 - 需要作为构造函数的场景。
- 需要函数动态上下文(如某些库通过函数上下文传入额外信息)。
结合对象方法的例子:
const counter = {
count: 0,
increment: () => {
this.count++; // 不生效,this 指向外层(可能是 window)
}
};
counter.increment();
console.log(counter.count); // 仍然是 0,错误用法
正确写法应为普通函数方法或使用 ES6 方法简写:
const counter = {
count: 0,
increment() {
this.count++;
}
};
9.5.4 小结
箭头函数不只是 shorter syntax,更是一种行为有本质区别的函数类型。它在带来 this 确定性和简洁性的同时,也放弃了普通函数的部分能力。掌握这些差异,你就可以放心地在恰当的场景中让代码更清晰易读,同时避免因错误使用而导致的棘手 Bug。