news 2026/5/12 7:28:03

面试官:5年经验还不懂箭头函数?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
面试官:5年经验还不懂箭头函数?


昨天看代码时,我遇到了一行看起来特别普通的 JavaScript。

普通到什么程度?

我第一眼扫过去,甚至觉得它不配让我停下来。

const data = (x) => x * 2 || 100;

我看完,笑了一下,准备继续往下读。

结果下一秒,我突然卡住了。

等等,data到底是什么?

我当时的第一反应是:“这还不简单?||会返回第一个 truthy 值。”

左边的(x) => x * 2是一个箭头函数。 箭头函数本质上是对象。 对象是 truthy。 所以,data应该就是这个函数本身:(x) => x * 2

至于后面的|| 100

死代码而已。

我甚至自信到,在测试之前就把这套推理写了下来。

然后,我打开了控制台。

let data = (x) => x * 2 || 100;
data(0)

输出:

100

再试一次:

data(5)

输出:

10

那一刻,我沉默了。

data(0)居然返回了100

如果data真的只是(x) => x * 2,那传入0,结果就应该是0。不可能是100

也就是说,我对这行代码的理解,错得很彻底。

我到底错在哪?

我当时是把这行代码读成了这样:

// 我以为 JavaScript 是这么理解的: const data = ((x) => x * 2) || 100;

按照这种读法,左边是一个函数,而函数是 truthy,所以data会直接拿到左边这个函数,右边的100根本不会执行。

逻辑看起来没毛病。

但问题是,JavaScript 根本不是这么解析的。

它真正看到的是:

// JavaScript 实际理解的是: const data = (x) => (x * 2 || 100);

注意,||并不在函数外面。

它在函数体里面。

箭头函数会把右边的整个表达式,都吞进自己的函数体里。

所以,data是一个函数。这个函数返回的是:

x * 2 || 100

当你传入0时:

0 * 2 || 100

也就是:

0 || 100

结果当然是:

100

当你传入5时:

5 * 2 || 100

也就是:

10 || 100

结果就是:

10

所以,我之前脑补出来的“死代码理论”,完全错了。

100不是被丢掉的分支,而是实打实会生效的兜底值。

为什么我们很容易想错?

因为大脑会下意识把=>当成一个普通的二元运算符。

好像它和+*||一样,只是夹在左右两边,用来参与优先级比较。

但它不是。

=>不是普通运算符。

它是箭头函数表达式的一部分,是一个语法边界。

一个简写函数体的箭头函数,语法大概是这样:

ArrowFunction → ArrowParameters => ConciseBody ConciseBody → AssignmentExpression

右边这个AssignmentExpression很“贪”。

只要它没遇到逗号、分号、右括号,或者输入结束,它就会继续往后吃。

它会吃掉:

x * 2 + 1

也会吃掉:

x || y

还会吃掉:

x && y

或者:

x ?? y

甚至三元表达式:

x ? a : b

以及赋值表达式:

y = 5

换句话说,一旦你写下=>,它右边剩下的那整段表达式,都会被当成函数体。

那里已经没有什么“外层的||”了。

||早就被吞进函数里了。

三个最容易踩坑的写法

1. 看起来像死代码的 fallback

const fee = (price) => price * 0.1 || 5;

你可能会把它理解成:

“计算手续费,如果price * 0.1没值,就兜底为5。”

这个理解没错。

但它之所以没错,是因为|| 5在函数体里面。

它绝不是说:

“如果函数本身是 truthy,就把fee赋值成函数,否则赋值成5。”

不是。

fee永远是函数。

只不过这个函数内部有一个 fallback。

2. 被箭头函数吞进去的三元表达式

const greet = (name) => name ? `Hi, ${name}` : "Hello stranger";

这整段三元表达式,都是函数体。

也就是说,它等价于:

const greet = (name) => (name ? `Hi, ${name}` : "Hello stranger");

这里的:不是 label。

?也不是 optional chaining。

它们都属于条件表达式,而这个条件表达式,就是箭头函数的返回值。

同样的坑,只是换了个运算符。

3. async 和 IIFE 风格造成的误读

const fn = async () => fetch('/api') || retry();

千万别把它读成:

“定义一个 async function,或者执行 retry。”

错。

真正的函数体是:

fetch('/api') || retry()

也就是说,理论上如果fetch('/api')返回 falsy,函数就会调用retry()

当然,实际中fetch返回的是 Promise 对象,而 Promise 是 truthy,所以retry()不会因为这个逻辑被触发。

但这里重点不是运行结果,而是解析方式。

|| retry()在函数里面,不在函数外面。

这道面试题到底在考什么?

如果面试官问你:

const data = (x) => x * 2 || 100; data(0) 返回什么?

他不是在考你记不记得||的短路规则。

这个你大概率知道。

他真正想看的是:你知不知道=>不是一个普通二元运算符。

它是语法边界。

它右边的内容,会成为函数体。

一旦你理解了这一点,这段代码就不再玄学了。

如果你写了几年 JavaScript,却一直潜意识里把=>当成和+||一样的东西来分析,那这道题刚好会把这个认知漏洞揪出来。

它不大。

但很真实。

而且,它出现的频率比你想象中高得多。

React 组件里的简写箭头函数、Array.prototype.map里的单行 callback、各种 reducer 里的表达式返回值,都可能藏着类似的误读风险。

我现在的心智模型

每次看到简写函数体的箭头函数:

const f = (args) => SOMETHING;

我都会在脑子里自动把它补成:

const f = (args) => (SOMETHING);

这个小动作非常有用。

它能立刻告诉你:函数体到底从哪里开始,到哪里结束。

||?:&&??这些东西,也会瞬间回到它们正确的位置——在函数体里面,而不是函数外面。

最后

=>不是普通运算符。

它是函数表达式的分隔符。

从它右边开始,一直到下一个逗号、分号,或者表达式结束,基本都属于函数体。

所以,下次有人拿这行代码问你:

const data = (x) => x * 2 || 100;

再问:

data(0)

你就不会再犹豫了。

答案是:

100

而且你现在不只是知道答案。

你知道它为什么是这个答案。

最后:

精通 React 面试:从零到中高级(针对面试回答)

CSS终极指南

Vue 设计模式实战指南

20个前端开发者必备的响应式布局

深入React:从基础到最佳实践完整攻略

python 技巧精讲

React Hook 深入浅出

CSS技巧与案例详解

vue2与vue3技巧合集

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/12 7:24:39

消息中间件控制平面:统一管理RabbitMQ与Kafka的声明式解决方案

1. 项目概述:一个面向开发者的消息中间件控制平面最近在折腾微服务架构下的消息通信,发现一个挺普遍的问题:虽然像 RabbitMQ、Kafka、RocketMQ 这类消息中间件本身功能强大,但管理起来却是个麻烦事。配置分散在各个服务的代码里&a…

作者头像 李华
网站建设 2026/5/12 7:23:08

免费一键去图片水印的App有哪些?2026实测免费去图片水印软件推荐

免费一键去图片水印的App有哪些?2026实测免费去图片水印软件推荐 图片带水印,用起来总让人头疼——截图分享嫌难看,素材二次编辑受阻,手机拍照附带时间戳,从平台转发的图片自带品牌标识……这些场景正在推动越来越多人…

作者头像 李华
网站建设 2026/5/12 7:19:16

FastAPI 最佳实践:构建高性能电商后端

# FastAPI 最佳实践:构建高性能电商后端 ## 引言 在当今电商领域,后端服务的响应速度和可扩展性直接决定了用户体验和业务增长。传统的 Django 或 Flask 虽然成熟,但在面对高并发、异步处理、类型安全等现代需求时,往往需要额外引…

作者头像 李华
网站建设 2026/5/12 7:09:35

深度解析开源AI工具库:OpenAI API封装库的设计与实战应用

1. 项目概述:一个开源AI工具库的深度解构最近在GitHub上看到一个名为“anasfik/openai”的项目,这个标题乍一看有点意思。它不像官方SDK那样直接叫“openai”,而是带上了个人或组织的命名空间前缀“anasfik/”。这通常意味着这是一个第三方封…

作者头像 李华