用箭头函数重写回调:现代 JavaScript 教程函数表达式精简实战

原创2026-09-30 18:13:59179 阅读
文章标签:文档/教程前端

用箭头函数重写回调:现代 JavaScript 教程函数表达式精简实战

在 1-js/02-first-steps/17-arrow-functions-basics/ 章节中,有一道经典入门练习题 "Rewrite with arrow functions":把 ask 函数调用处两个匿名的 Function Expression 回调改写为箭头函数,以体会箭头语法在回调场景下的简洁性。本文以该练习及其官方解答(1-rewrite-arrow/solution.md)为主线,结合同章节 article.md 与后续深入章节,系统讲解箭头函数的基础语法、回调中的应用方式及其背后的运行机制,读完你能够熟练地在回调、单行动作等场景中用箭头函数替换冗长的函数表达式,并理解其边界。

任务回顾:等待重写的 ask 回调代码

练习题(1-rewrite-arrow/task.md)给出了如下代码,要求把两个匿名函数表达式替换为箭头函数:

function ask(question, yes, no) {
  if (confirm(question)) yes();
  else no();
}

ask(
  "Do you agree?",
  function() { alert("You agreed."); },
  function() { alert("You canceled the execution."); }
);

这段代码的背景来自前一章 16-function-expressions/article.md 的"回调函数"小节:ask 接收三个参数——question 是问题文本,yes 与 no 分别是用户确认/取消后要执行的函数。ask 内部根据 confirm(question) 的结果调用 yes() 或 no(),这种"把函数作为值传入、稍后再被调用"的函数就被称为回调函数(callback)。

当前版本的问题在于:每个回调都要写成完整的 function() { ... },占用了两行且略显冗长。这正是箭头函数最典型的应用场景——为单行动作与回调而生的精简语法。

参考解答:一行一个回调

官方解答(1-rewrite-arrow/solution.md)如下:

function ask(question, yes, no) {
  if (confirm(question)) yes();
  else no();
}

ask(
  "Do you agree?",
  () => alert("You agreed."),
  () => alert("You canceled the execution.")
);

改动点与效果:

  • 两个回调参数由 function() { ... } 变为 () => alert(...);
  • 因为箭头函数体是一个单一表达式 alert(...),它会被自动求值并作为返回值(此处返回值无实际用途,但表达式本身会被执行),所以可以省去花括号与 return;
  • 因为回调没有参数,所以空括号 () 必须保留(这是箭头函数对零参数的硬性要求);
  • 整体从 4 行缩到 2 行,语义不变:"看起来又短又干净,对吧?"(原解答的结语)。

箭头函数基础语法:三种形态

掌握这道题的前提,是先理解箭头函数的基本形态。该章节的 article.md 给出了完整的入门语法,共三种写法:

1. 多参数 + 表达式体

let func = (arg1, arg2, ..., argN) => expression;

这等价于:

let func = function(arg1, arg2, ..., argN) {
  return expression;
};

实际例子:

let sum = (a, b) => a + b;

alert( sum(1, 2) ); // 3

(a, b) => a + b 表示一个接收两个参数的函数,执行时求值右侧表达式 a + b 并返回结果——不需要显式 return。

2. 单参数:可省略括号

如果只有一个参数,圆括号可以省掉,使写法更短:

let double = n => n * 2;
// 大致等价于:let double = function(n) { return n * 2 }

alert( double(3) ); // 6

3. 零参数:必须保留空括号

let sayHi = () => alert("Hello!");

sayHi();

这正是本练习中两个回调的写法——回调不需要任何参数,因此写成 () => ...,空括号不可省略。

多行箭头函数:花括号 + 显式 return

当函数体需要多条语句时,用花括号包起来。此时箭头函数与普通函数一样,必须有显式 return 才能返回值:

let sum = (a, b) => {  // 花括号开启多行函数体
  let result = a + b;
  return result;       // 使用花括号时必须显式 return
};

alert( sum(1, 2) ); // 3

两条形态规则可以这样记忆:

  • 无花括号:(...args) => expression——右侧是表达式,函数求值并返回其结果;单参数时括号可省略,如 n => n*2;
  • 有花括号:(...args) => { body }——可在体内写多条语句,但要返回内容必须显式写 return。

为什么回调场景最适合箭头函数

本练习展示了箭头函数的核心理念:JavaScript 充满"写一个小函数并交给别处执行"的场景——arr.forEach(func) 由 forEach 对每个数组元素调用 func,setTimeout(func) 由内置调度器调用 func。而箭头函数正是为这类"简短代码块"设计的,它不在当前上下文之外另立门户。

以本练习的 ask 为例,将 16-function-expressions/article.md 中同名示例的回调改写后,代码的意图("同意"或"取消"时各弹一条提示)变得更加一目了然,这是箭头函数在回调与单行动作上被广泛使用的原因。同章节 article.md 还展示了箭头函数作为函数值参与三元运算的动态创建:

let age = prompt("What is your age?", 18);

let welcome = (age < 18) ?
  () => alert('Hello!') :
  () => alert("Greetings!");

welcome();

可以看到,箭头函数可以像 Function Expression 一样在任意表达式中被创建、赋值与传递。

深入机制:箭头函数没有自己的 this、arguments 与 super

入门章节 article.md 明确提示"箭头函数还有更多特性,留待后续深入",对应的进阶章节是 06-advanced-functions/12-arrow-functions/article.md。理解这些特性,才能判断"何时该用、何时不该用"箭头函数。

没有自己的 this:箭头函数内部访问 this 时,取值来自外层词法环境。例如在对象方法里用箭头函数遍历数组,this.title 与外层方法中的 this 完全相同;若改用普通匿名函数,forEach 默认以 this=undefined 调用它,访问 this.title 会直接报错(Cannot read property 'title' of undefined)。这也意味着箭头函数不能用作构造函数,不能以 new 调用。

没有自己的 arguments:这反而让装饰器(decorator)写法更简洁——内部箭头函数可以直接沿用外层函数的 this 和 arguments,无需像普通函数那样先用 args、ctx 两个变量暂存:

function defer(f, ms) {
  return function() {
    setTimeout(() => f.apply(this, arguments), ms);
  };
}

没有 super:这与后续 09-classes 章节中的类继承相关,属于进阶内容。

把这些特性与本练习联系起来,可以得出一个实用判断标准:

  • 适合箭头函数:短小的回调、单行动作、需要在当前上下文(当前 this)中工作的代码;
  • 不适合箭头函数:需要自己的 this(如对象方法)、需要作为构造函数使用、需要访问自身 arguments 的场景。

常见误区与自查清单

结合本练习与章节内容,重写时最容易踩的坑有四个:

  1. 零参数忘写空括号:() => alert(...) 的 () 不能省略,写成 => alert(...) 是语法错误;
  2. 表达式体误加花括号后忘记 return:(a, b) => { a + b } 不会返回 a + b,需要 return;
  3. 以为箭头函数完全等同于普通函数:它没有自己的 this、arguments、super,也不能 new;
  4. 回调语义未变但作用域变了:在需要"保留外层 this"的回调中,箭头函数反而更安全(参考上面 showList 的例子)。

小结

本练习 (1-rewrite-arrow/solution.md) 用两行改动说明了箭头函数的核心价值:把 ask 的回调从 function() { ... } 压缩为 () => ...,代码更短、意图更清晰。结合入门章节 article.md 的语法体系与进阶章节 12-arrow-functions/article.md 的运行机制,可以归纳出完整的实践结论:

  • 箭头函数有两种形态:无花括号的表达式体(自动返回)与带花括号的多行体(需显式 return);
  • 单参数可省略括号,零参数必须保留空括号;
  • 箭头函数天然适合回调、单行动作等"在当前上下文工作"的场景;
  • 它没有自己的 this、arguments 和 super,不能用 new 调用,因此在需要独立上下文的场合应改用普通函数。
登录后查看全文
en.javascript.info