箭头函数 vs 普通函数:不只是写法简洁
很多人觉得箭头函数只是语法糖,但它在 this 指向、arguments 绑定以及构造函数行为上的差异,才是你必须掌握的核心。
箭头函数 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 微微一笑:“但我活得简单,从来不迷失方向。”