返回首页

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);

面试要点

  1. this 绑定:箭头函数没有自己的 this,它继承外层作用域的 this
  2. 不能用作构造函数:箭头函数不能使用 new 关键字
  3. 没有 arguments:使用剩余参数 ...args 替代
  4. 适用场景:回调函数、数组方法、Promise 链
  5. 不适用场景:需要动态 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 改变)