25. 说说你对箭头函数的理解?
问题解析
箭头函数(Arrow Function)是 ES6 引入的一种新的函数定义方式,它提供了一种更简洁的语法来编写函数表达式。面试中考察箭头函数,主要关注其语法特性、this 绑定机制以及与普通函数的区别。
核心概念
1. 基本语法
箭头函数使用 => 符号定义,语法更加简洁:
// 传统函数
function add(a, b) {
return a + b;
}
// 箭头函数
const add = (a, b) => a + b;
// 单个参数可以省略括号
const square = x => x * x;
// 无参数需要空括号
const greet = () => "Hello World";
// 多行语句需要使用花括号并显式返回
const sum = (a, b) => {
const result = a + b;
return result;
};
2. 箭头函数的特点
| 特性 | 箭头函数 | 普通函数 |
|---|---|---|
| this 绑定 | 词法作用域,继承外层 this | 运行时动态绑定 |
| arguments 对象 | 没有自己的 arguments | 有自己的 arguments |
| 构造函数 | 不能作为构造函数使用 | 可以作为构造函数 |
| prototype 属性 | 没有 prototype | 有 prototype |
| 适用场景 | 回调函数、短小的函数 | 通用场景 |
详细解答
1. this 的绑定机制
箭头函数最大的特点是没有自己的 this,它会捕获其所在上下文的 this 值:
// 普通函数中的 this
const obj = {
name: "Alice",
sayHello: function() {
console.log("Hello, " + this.name);
}
};
obj.sayHello(); // "Hello, Alice"
// 箭头函数中的 this
const obj2 = {
name: "Bob",
sayHello: () => {
console.log("Hello, " + this.name);
}
};
obj2.sayHello(); // "Hello, undefined" (this 指向全局对象)
// 正确的使用方式
const obj3 = {
name: "Charlie",
sayHello: function() {
setTimeout(() => {
console.log("Hello, " + this.name);
}, 100);
}
};
obj3.sayHello(); // "Hello, Charlie" (箭头函数继承了 sayHello 的 this)
2. 没有 arguments 对象
// 普通函数
function regular() {
console.log(arguments);
}
regular(1, 2, 3); // [1, 2, 3]
// 箭头函数
const arrow = () => {
console.log(arguments); // ReferenceError: arguments is not defined
};
// 解决方案:使用剩余参数
const arrow2 = (...args) => {
console.log(args);
};
arrow2(1, 2, 3); // [1, 2, 3]
3. 不能作为构造函数
const Person = (name) => {
this.name = name;
};
// const p = new Person("Alice");
// TypeError: Person is not a constructor
4. 没有 prototype 属性
const fn = () => {};
console.log(fn.prototype); // undefined
深入理解
箭头函数的词法作用域
箭头函数的 this 在定义时就已经确定,而不是在调用时:
const obj = {
value: 42,
getValue: function() {
return this.value;
},
getValueArrow: () => {
return this.value;
}
};
console.log(obj.getValue()); // 42
console.log(obj.getValueArrow()); // undefined
// 箭头函数的 this 继承自定义时的上下文
const obj2 = {
value: 100,
getValue: obj.getValue,
getValueArrow: obj.getValueArrow
};
console.log(obj2.getValue()); // 100 (运行时绑定)
console.log(obj2.getValueArrow()); // undefined (定义时绑定)
箭头函数与闭包
function Counter() {
this.count = 0;
// 箭头函数保持对 Counter 的 this 的引用
setInterval(() => {
this.count++;
console.log(this.count);
}, 1000);
}
const counter = new Counter();
// 输出 1, 2, 3, ... (箭头函数正确捕获了 Counter 的 this)
最佳实践
1. 适用场景
// 1. 回调函数
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2);
// 2. 链式操作
const result = numbers
.filter(n => n > 2)
.map(n => n * 2)
.reduce((sum, n) => sum + n, 0);
// 3. Promise 链
fetch('/api/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
// 4. 事件处理(需要保持 this 上下文)
class Button {
constructor() {
this.clicks = 0;
document.querySelector('button').addEventListener('click', () => {
this.clicks++;
console.log(`Clicked ${this.clicks} times`);
});
}
}
2. 不适用场景
// 1. 需要动态 this 的场景
const obj = {
name: "Alice",
// 错误:箭头函数无法访问 obj 的 name
greet: () => `Hello, ${this.name}`
};
// 正确做法
const obj2 = {
name: "Alice",
greet: function() {
return `Hello, ${this.name}`;
}
};
// 2. 需要 arguments 的场景
function logArgs() {
// 错误:箭头函数没有 arguments
const arrow = () => {
console.log(arguments);
};
arrow();
}
// 正确做法
function logArgs2() {
const arrow = (...args) => {
console.log(args);
};
arrow(1, 2, 3);
}
// 3. 需要作为构造函数的场景
// 错误
const Person = (name) => {
this.name = name;
};
// 正确
function Person2(name) {
this.name = name;
}
3. 代码风格建议
// 1. 单行表达式省略 return 和花括号
const add = (a, b) => a + b;
// 2. 对象字面量需要括号
const makeObject = (name, age) => ({ name, age });
// 3. 复杂逻辑使用花括号和显式 return
const processData = (data) => {
const filtered = data.filter(item => item.active);
const mapped = filtered.map(item => item.name);
return mapped.join(', ');
};
// 4. 数组方法中使用
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 }
];
const names = users
.filter(user => user.age >= 25)
.map(user => user.name);
面试要点
- this 绑定:箭头函数没有自己的 this,它继承外层作用域的 this
- 不能用作构造函数:箭头函数不能使用 new 关键字
- 没有 arguments:使用剩余参数
...args替代 - 适用场景:回调函数、数组方法、Promise 链
- 不适用场景:需要动态 this 的对象方法、构造函数
常见面试题
// 面试题 1:输出什么?
const obj = {
name: "Alice",
sayHi: () => {
console.log(`Hi, ${this.name}`);
},
sayHello: function() {
console.log(`Hello, ${this.name}`);
}
};
obj.sayHi(); // "Hi, undefined" (this 指向全局)
obj.sayHello(); // "Hello, Alice"
// 面试题 2:如何实现一个可以访问 this 的延迟函数?
class Timer {
constructor() {
this.seconds = 0;
}
start() {
// 使用箭头函数保持 this
setInterval(() => {
this.seconds++;
console.log(this.seconds);
}, 1000);
}
}
// 面试题 3:箭头函数与 bind 的区别
function regular() {
return this.value;
}
const arrow = () => this.value;
const obj = { value: 42 };
console.log(regular.bind(obj)()); // 42
console.log(arrow.bind(obj)()); // undefined (箭头函数的 this 无法被 bind 改变)