Skip to content

箭头函数和普通函数在JavaScript中的区别是什么?

JavaScript的箭头函数表面上看起来与普通函数相同,但它们有一些非常重要的区别:

  • 语法上的区别
  • this 值(执行上下文)
  • 用作方法的用法
  • 用作构造函数的用法
  • arguments 绑定

语法

箭头函数和普通函数之间最明显的区别是它们的语法。它们不仅外观不同,而且箭头函数还提供了隐式返回的简写形式,并且允许省略单个参数周围的括号。

const square = a => a * a;

// 等效的普通函数
function square(a) {
  return a * a;
}

执行上下文

在普通函数内部,执行上下文(即 this 的值)是动态的。这意味着 this 的值取决于函数的调用方式(简单调用、方法调用、间接调用或构造函数调用)。另一方面,箭头函数不定义自己的执行上下文。这导致箭头函数的 this 在词法上解析(即在箭头函数定义的作用域内)。

function logThis() {
  console.log(this);
}
document.addEventListener('click', logThis);
// `this` 指向 document

const logThisArrow = () => {
  console.log(this);
};
document.addEventListener('click', logThisArrow);
// `this` 指向全局对象

`Function.prototype.call()`、`Function.prototype.bind()` 和 `Function.prototype.apply()` 也不能正确地与箭头函数一起使用。它们的目的是允许方法在不同的作用域中执行,但是箭头函数的 `this` 值无法改变,因为它是在词法上解析的。

```js
function logThis() {
  console.log(this);
}
logThis.call(42);       // 输出:42

const logThisArrow = () => {
  console.log(this);
};
logThisArrow.call(42);  // 输出全局对象

方法

由于箭头函数不定义自己的执行上下文,所以它们不适合用作方法。然而,由于 Class fields proposal 的存在,如果您的环境支持,箭头函数可以在类中用作方法。

const obj = {
  x: 42,
  logThisX: function() {
    console.log(this.x, this);
  },
  logThisXArrow: () => {
    console.log(this.x, this);
  }
};

obj.logThisX();       // 输出:42, Object {...}
obj.logThisXArrow();  // 输出:undefined, 全局对象

构造函数

普通函数可以使用 new 关键字作为构造函数。由于箭头函数内部的 this 是词法解析的结果,所以它们不能用作构造函数。使用 new 关键字和箭头函数会导致 TypeError

function Foo(bar) {
  this.bar = bar;
}
const a = new Foo(42);  // Foo {bar: 42}

const Bar = foo => {
  this.foo = foo;
};
const b = new Bar(42);  // TypeError: Bar is not a constructor

参数

另一个区别是arguments对象的绑定。与普通函数不同,箭头函数没有自己的arguments对象。一个绕过这个限制的现代替代方法是使用剩余参数。

function sum() {
  return arguments[0] + arguments[1];
};
sum(4, 6);        // 10

const arguments = [1, 2, 3];
const sumArrow = () => {
  return arguments[0] + arguments[1];
};
sumArrow(4, 6);   // 3 (解析为 1 + 2)

const sumRest = (...arguments) => {
  return arguments[0] + arguments[1];
}
sumRest(4, 6);    // 10

其他区别

最后,还有一些不太重要但值得一提的其他区别。这些包括箭头函数中缺少prototype属性,以及箭头函数体中不能使用yield关键字。后者的结果是箭头函数不能用作生成器。