我不再在 JavaScript 里把所有东西链在一起
摘要
开发者 Matt Smith 解释,为了调试更轻松、性能更好,他现在在 JavaScript 中更偏爱一步步写代码,而不是冗长的方法链。
暂无内容
查看缓存全文
缓存时间: 2026/04/22 13:13
# 为什么我不再在 JavaScript 里把所有东西串成链了 - Matt Smith
来源:https://allthingssmitty.com/2026/04/20/why-i-dont-chain-everything-in-javascript-anymore/
我以前经常这么写 JavaScript:
```js
const result = users
.filter(user => user.active)
.map(user => user.name)
.sort()
.slice(0, 5);
```
这段代码本身没错,我以前一直这么写。但正是这种写法,一开始看着挺舒服,后来越来越难维护。
## 链式调用很香……直到它不香
问题不在 `map()` 或 `filter()`,而在于你把它们叠罗汉。你写的不再是“步骤”,而是“管道”。
管道看着整洁,可你仍得在脑子里一步步走:filter → map → sort → slice。
偶尔来一回还行,满文件都是就容易让人崩溃。
对比一下:
```js
const activeUsers = users.filter(user => user.active);
const names = activeUsers.map(user => user.name);
names.sort();
return names.slice(0, 5);
```
行数多了,但每一步都摆在那儿,不用解码。
## 同一个需求,三种写法
**链式:**
```js
users.filter(u => u.active).map(u => u.name)[0]
```
看着利落,我以前常用。可它把整张表都过一遍,其实我只想要一条数据。
**分步:**
```js
const user = users.find(u => u.active);
const name = user?.name;
```
我通常选这种。早停早省力,哪不对劲也能逐点排查。
**完全掌控:**
```js
for (const u of users) {
if (u.active) return u.name;
}
```
最直白,有时候也是最容易看懂的写法。
## 调试时最闹心
一旦要调试,链式的问题立刻放大。
想瞄一眼过滤后的结果?你得这么干:
```js
const result = users
.filter(user => {
console.log(user);
return user.active;
})
.map(user => user.name);
```
逻辑和调试代码混成一团,最后只能把链拆散。
## 你可能白干了活
链式天然倾向于“全量处理”,哪怕你并不需要。
```js
const firstActiveUser = users
.filter(user => user.active)
.map(user => user.name)[0];
```
它把整张表过滤、映射,再取第一项。
其实你只想:
```js
const user = users.find(user => user.active);
const name = user?.name;
```
或者:
```js
for (const user of users) {
if (user.active) {
return user.name;
}
}
```
## 代价在哪
这不只是可读性。数组一大或路径一热,多余的开销就肉眼可见。长链在线上调试也出奇地痛苦。
我以前写过不少“巨型链”,回头看……挺惭愧的。
## 流畅 ≠ 清晰
链式受欢迎,是因为初读顺口:
```js
data
.transform()
.normalize()
.validate()
.save();
```
可读完后你开始猜:每一步返回啥?断点往哪打?能不能复用?
拆成步骤,这些疑问立刻有答案。
### 异步链也一个德行
Promise 链看着优雅:
```js
const data = await fetchUsers()
.then(res => res.json())
.then(users => users.filter(u => u.active))
.then(users => users.map(u => u.name));
`
可你把异步控制(取数据、解析)和数据变换全混在一条链里。
拆开更易读:
```js
const res = await fetchUsers();
const users = await res.json();
const activeNames = users.filter(u => u.active).map(u => u.name);
```
## 我的土味经验法则
链长|建议|示例
---|---|---
1 步|随便写|`users.map(u => u.name)`
2 步|通常 OK|`users.filter(u => u.active).map(u => u.name)`
3–4 步|停一停,考虑拆|`users.filter(...).map(...).sort(...).slice(...)`
5+ 步|必须拆|复杂变换或异步链
## 不是让你别链
短链我照写。一到三四步,我就踩刹车。
### 我现在怎么想
写代码时链式快;读代码时步骤爽。两者不是一回事。
### 我通常怎么拆
步骤|怎么做|示例
---|---|---
1|给中间结果起名|`const activeUsers = users.filter(u => u.active)`
2|逻辑拆分|`const names = activeUsers.map(u => u.name)`
3|只链清楚的部分|`names.sort()`
这招替我省了不少头疼。
JavaScript 工具箱很大,但不必一次全倒出来。
- JavaScript (https://allthingssmitty.com/tags/javascript)
相似文章
不久我们就能终于将 JavaScript 放逐至 ShadowRealm
本文探讨了 TC39 提出的 ShadowRealm 提案,该提案旨在允许在不使用 iframe 或 Web Workers 的情况下,在隔离环境(Realm)中执行 JavaScript,从而改善代码沙盒机制并提升性能。
就他妈用 React
这篇主观性很强的文章激进地主张在复杂 Web 应用中使用现代 JavaScript 框架(如 React)而非纯 HTML,认为复杂性需要合适的工具。
我决定回归手写代码
作者在重构一个 Kubernetes 仪表盘工具时反思道,虽然借助 AI 进行“氛围编程”(vibe-coding)能加速功能开发,但在缺乏人工监督的情况下,往往会导致架构臃肿和技术债务。
不再那么锁定了
一篇博文讨论了编程语言的锁定效应如何因编码代理的兴起而减弱,以一家公司将原生iPhone和Android应用重写为React Native为例。
只有我开始对“氛围编码”感到疲惫了吗?
一位开发者分享了对“氛围编码”的倦怠感,指出虽然 AI 代理能加快初期开发速度,但在复杂的代码库中会引入显著的架构调试挑战和技术债务。