JavaScript 中的函数是一等公民,可以作为变量传递、作为参数传入其他函数,或者作为返回值。ES6 引入的箭头函数为我们提供了一种更简洁的函数语法,但它与传统的函数声明有着本质的区别。
语法差异
普通函数
// 函数声明
function add(a, b) {
return a + b;
}
// 函数表达式
const multiply = function(a, b) {
return a * b;
};
箭头函数
// 基本语法
const add = (a, b) => {
return a + b;
};
// 简化返回语句
const add = (a, b) => a + b;
// 单个参数可省略括号
const double = n => n * 2;
// 无参数需要空括号
const getRandomNumber = () => Math.random();
核心区别
1. this 绑定机制
普通函数:this 值取决于函数如何被调用,可以通过 call、apply 或 bind 方法改变。
const user = {
name: '张三',
greet: function() {
console.log(`你好,我是 ${this.name}`);
}
};
user.greet(); // 输出: 你好,我是 张三
const greetFunc = user.greet;
greetFunc(); // 输出: 你好,我是 undefined (在非严格模式下,this 指向全局对象)
箭头函数:没有自己的 this,它会继承定义时所在上下文的 this 值,且无法通过 call、apply 或 bind 改变。
const user = {
name: '张三',
greet: () => {
console.log(`你好,我是 ${this.name}`);
}
};
user.greet(); // 输出: 你好,我是 undefined (this 指向定义时的上下文,通常是全局对象)
const user2 = {
name: '张三',
greetRegular: function() {
// 这里的 this 指向 user2
const greetArrow = () => {
console.log(`你好,我是 ${this.name}`);
};
greetArrow();
}
};
user2.greetRegular(); // 输出: 你好,我是 张三
2. 构造函数
普通函数:可以作为构造函数使用,使用 new 关键字创建实例。
function Person(name) {
this.name = name;
}
const person = new Person('张三');
console.log(person.name); // 输出: 张三
箭头函数:不能作为构造函数使用,没有 prototype 属性。
const Person = (name) => {
this.name = name;
};
const person = new Person('张三'); // TypeError: Person is not a constructor
3. arguments 对象
普通函数:有自己的 arguments 对象,包含传递给函数的所有参数。
function sum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
console.log(sum(1, 2, 3, 4)); // 输出: 10
箭头函数:没有自己的 arguments 对象,但可以使用剩余参数(rest parameters)。
const sum = (...args) => {
return args.reduce((total, current) => total + current, 0);
};
console.log(sum(1, 2, 3, 4)); // 输出: 10
4. 方法定义
普通函数:适合作为对象方法,特别是需要访问对象属性时。
const calculator = {
value: 0,
add(n) {
this.value += n;
return this.value;
}
};
console.log(calculator.add(5)); // 输出: 5
箭头函数:不适合作为对象方法,因为 this 不会指向对象本身。
const calculator = {
value: 0,
add: (n) => {
this.value += n; // this 不指向 calculator
return this.value;
}
};
console.log(calculator.add(5)); // 输出: NaN,因为 this.value 是 undefined
5. 生成器函数
普通函数:可以是生成器函数。
function* generateSequence() {
yield 1;
yield 2;
yield 3;
}
const generator = generateSequence();
console.log(generator.next().value); // 输出: 1
箭头函数:不能是生成器函数。
const generateSequence = *() => { // 语法错误
yield 1;
yield 2;
yield 3;
};
使用场景建议
适合使用箭头函数的场景
- 简短的回调函数:特别是在数组方法中。
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2);
console.log(doubled); // 输出: [2, 4, 6, 8, 10]
- 需要保留外部
this上下文的回调:避免使用var self = this或bind方法。
function Counter() {
this.count = 0;
// 使用箭头函数保留 this 上下文
setInterval(() => {
this.count++;
console.log(this.count);
}, 1000);
}
const counter = new Counter(); // 每秒输出递增的数字
- 链式方法调用:使代码更简洁。
const result = [1, 2, 3, 4, 5]
.filter(n => n % 2 === 0)
.map(n => n * 2)
.reduce((sum, n) => sum + n, 0);
console.log(result); // 输出: 12
- 简单的工具函数:不需要
this上下文的纯函数。
const isEven = num => num % 2 === 0;
const capitalize = str => str.charAt(0).toUpperCase() + str.slice(1);
适合使用普通函数的场景
- 对象方法:需要访问对象本身的属性。
const person = {
name: '张三',
sayHello() {
console.log(`你好,我是 ${this.name}`);
}
};
- 构造函数:创建对象实例。
function User(name, age) {
this.name = name;
this.age = age;
}
User.prototype.getInfo = function() {
return `${this.name}, ${this.age}岁`;
};
- 需要动态
this绑定的函数:根据调用方式改变this指向。
function greet() {
console.log(`你好,${this.name}`);
}
const person1 = { name: '张三' };
const person2 = { name: '李四' };
greet.call(person1); // 输出: 你好,张三
greet.call(person2); // 输出: 你好,李四
- 事件处理函数:需要访问事件目标。
document.getElementById('button').addEventListener('click', function(event) {
console.log(this); // 指向按钮元素
console.log(event.target); // 同样指向按钮元素
});
- 生成器函数:需要使用
yield关键字。
function* paginate(items, pageSize) {
let page = 0;
while (page * pageSize < items.length) {
yield items.slice(page * pageSize, (page + 1) * pageSize);
page++;
}
}
性能考虑
从性能角度看,箭头函数和普通函数之间没有显著差异。选择使用哪种函数类型应该基于语法、this 绑定和其他功能需求,而不是性能考虑。
总结
箭头函数和普通函数各有优缺点,选择使用哪种取决于具体的使用场景:
- 如果需要
this指向定义函数的上下文,使用箭头函数 - 如果需要函数作为构造函数、方法或需要动态
this绑定,使用普通函数 - 对于简短的回调函数和不需要
this的工具函数,箭头函数通常是更好的选择