箭头函数 vs 普通函数:不只是写法简洁

很多人觉得箭头函数只是语法糖,但它在 this 指向、arguments 绑定以及构造函数行为上的差异,才是你必须掌握的核心。

6 分钟阅读
小明

箭头函数 vs 普通函数:不只是写法简洁

自从 ES6 带来了箭头函数(Arrow Function),前端代码变得越来越漂亮了。

// 普通函数
const add = function(a, b) {
  return a + b;
};

// 箭头函数
const add = (a, b) => a + b;

看着这简洁的语法,你是不是觉得它只是个“语法糖”?如果你真的这么想,那面试官可能会露出慈祥(危险)的微笑。

今天,小明就带你看看箭头函数和普通函数之间,那几条跨不过去的“鸿沟”。


一、最核心的区别:this 指向

这是两者最大的不同。

1.1 普通函数:谁调用我,我就指向谁

普通函数的 this 是动态的,是在运行时根据调用它的上下文决定的。

const xiaoming = {
  name: '小明',
  sayName: function() {
    console.log(this.name);
  }
};
xiaoming.sayName(); // '小明'

1.2 箭头函数:我没有 this,我只看我出生的地方

箭头函数本身没有自己的 this。它的 this 是在定义时捕获自外层作用域的。

const xiaoming = {
  name: '小明',
  sayName: () => {
    console.log(this.name);
  }
};
xiaoming.sayName(); // undefined (指向了全局作用域)

小明的小贴士: 在 React 或是事件回调里,如果你厌倦了用 bind(this),箭头函数就是你的最佳选择。


二、能不能当“造物主”?(构造函数)

普通函数可以作为构造函数,通过 new 关键字创建实例。

function Person(name) {
  this.name = name;
}
const p = new Person('小明'); // ✅

箭头函数不能作为构造函数。如果你尝试 new 一个箭头函数,JavaScript 会直接报错:TypeError: ... is not a constructor

为什么?因为箭头函数没有 prototype 属性,也没有 [[Construct]] 内部方法。


三、消失的 arguments

普通函数里,你可以访问一个叫 arguments 的类数组对象,它包含了所有传进来的参数。

function sum() {
  console.log(arguments);
}
sum(1, 2, 3); // [1, 2, 3]

箭头函数没有自己的 arguments。如果你需要获取所有参数,建议使用 ES6 的剩余参数(Rest Parameters)

const sum = (...args) => {
  console.log(args);
};
sum(1, 2, 3); // [1, 2, 3]

四、能不能改命?(call, apply, bind)

由于箭头函数的 this 是静态的,已经写死在它的基因里了,所以你无法通过 call(), apply()bind() 来改变它的 this

const fn = () => console.log(this.name);
fn.call({ name: '大佬' }); // 依然是 undefined (或者全局的 name)

虽然代码不会报错,但 call 的第一个参数会被静默忽略。


总结

特性普通函数箭头函数
this 指向动态(调用时决定)静态(定义时决定)
构造函数可以不可以
arguments没有(用 ...args)
prototype没有
语法较繁琐极简

小明建议:

  • 方法函数(对象的方法):用普通函数,如果你需要 this 指向该对象。
  • 回调函数高阶函数:首选箭头函数,逻辑清晰且 this 不迷路。
  • 构造函数:必须用普通函数。

“为什么箭头函数没有 this?” “因为它是一个纯粹的流浪者,总是依附于它的故乡。” —— 小明

最后,送你一个冷笑话: 函数 A 对箭头函数 B 说:“你真是个怪人,没脾气(this),没行李(arguments),还不想要孩子(constructor)。” 箭头函数 B 微微一笑:“但我活得简单,从来不迷失方向。”