📜 卷③ JavaScript 与 TypeScript
20 条。适用场景:语言和运行时层面现在有什么新工具,以及哪些老做法可以退休了。
「要点」「我的补充」为本人撰写,非原文摘录。
一、语言新能力:可以删掉依赖了
41. JavaScript Temporal is coming
来源:MDN Blog · developer.mozilla.org 读它解决什么:Date 的设计缺陷你忍了很多年,Temporal 是正式的替代品。
要点
Date的核心问题:月份从 0 开始、可变对象、时区处理弱、解析行为依赖实现- Temporal 用一组明确区分的类型解决:
PlainDate(无时区日期)、PlainDateTime、ZonedDateTime(带时区)、Instant(时间点)、Duration - 全部不可变,运算返回新对象,这消除了一大类隐蔽 bug
我的补充:类型区分是它最大的价值——"生日"应该是 PlainDate(不带时区,不然跨时区显示会差一天),"会议时间"应该是 ZonedDateTime。用 Date 存生日导致的跨时区差一天是最经典的日期 bug。迁移策略:新代码直接用 Temporal(需要 polyfill 时用 temporal-polyfill),旧代码不必急着改。用上它之后 date-fns、dayjs 这类库多数场景可以删掉。
42. New JavaScript Set methods
来源:MDN Blog · developer.mozilla.org 读它解决什么:集合的交集、并集、差集终于原生支持,不用再手写。
要点
- 新增
union、intersection、difference、symmetricDifference,以及判定方法isSubsetOf、isSupersetOf、isDisjointFrom - 此前这些操作要靠
[...a].filter(x => b.has(x))之类的写法,可读性和性能都不理想 - 参数可以是任何"类 Set"对象(有
size、has、keys)
我的补充:可读性提升很直接:a.intersection(b) 对比 new Set([...a].filter(x => b.has(x)))。实际用得最多的是权限判断——userPerms.isSupersetOf(requiredPerms) 比循环清楚得多。注意这些方法返回新 Set,不修改原对象。老环境的话 core-js 有 polyfill,但先想想是不是可以直接用数组解决,小集合上差别不大。
43. Locale-sensitive text segmentation with Intl.Segmenter
来源:MDN Blog · developer.mozilla.org 读它解决什么:按"字符"截断字符串结果乱了——emoji 变方块、中文和英文的分词规则不同。
要点
- JS 字符串是 UTF-16 码元序列,
.length与[i]都不是"用户看到的字符",emoji 和组合字符会被切坏 Intl.Segmenter按语言规则切分:grapheme(用户感知字符)、word(词)、sentence(句)- 中日韩没有空格分词,
word粒度必须靠这类 API
我的补充:截断字符串做摘要时必须用 grapheme 粒度,否则 emoji(尤其带肤色修饰符、ZWJ 组合的家庭 emoji)会被切成乱码方块。'👨👩👧'.length 是 8,用 slice(0, 4) 就毁了。中文摘要计算"字数"也该用它,而不是 .length。这个博客的文章摘要如果做自动截断,就该用这个而不是 substring。
44. Creating color palettes with the CSS color-mix() function
来源:MDN Blog · developer.mozilla.org 读它解决什么:主题色需要程序化派生,想知道 CSS 侧和 JS 侧该怎么配合。
要点
color-mix()在指定色彩空间里按比例混合两色,可以从一个基色派生整套明暗变体- 混合所用的色彩空间影响结果:
oklch的插值比srgb更符合视觉预期 - 与自定义属性配合,可以做到"改一个变量整站换色"
我的补充:动态主题(用户自选主题色)的现代实现就是它——JS 只需写一个 --brand 变量,其余色阶全交给 CSS 派生,比在 JS 里算颜色再注入十几个变量干净得多。要点是混合空间选 oklch:color-mix(in oklch, var(--brand), white 20%) 得到的浅色比 in srgb 自然得多,后者容易发灰。
45. Image formats: Codecs and compression tools
来源:MDN Blog · developer.mozilla.org 读它解决什么:该用 WebP 还是 AVIF,以及压缩参数怎么定。
要点
- 有损与无损、逐行与渐进、编码耗时与解码耗时是几个独立的维度,不能只看文件大小
- AVIF 压缩率通常优于 WebP,但编码慢得多,且解码在低端设备上有成本
- 工具链选择(
sharp、squoosh、cwebp)影响可自动化程度
我的补充:实用结论:照片类用 AVIF 或 WebP,图标和纯色图形用 SVG,截图类 WebP 无损常常小于 PNG。用 <picture> 加多个 <source> 让浏览器挑,兼容性问题自然解决。这个博客的壁纸全部是 .webp,25 张图对首屏加载影响明显小于 JPEG。批量转换用 sharp 脚本比手工快得多,注意保留原图以便日后重新编码。
46. Image formats: Pixel data from encoders to decoders
来源:MDN Blog · developer.mozilla.org 读它解决什么:理解图片编解码的原理,从而知道压缩参数为什么这样影响质量。
要点
- 有损压缩的核心手段是丢弃人眼不敏感的信息:色度子采样、频域变换后量化
- 因此图片内容决定最优参数:平滑渐变和锐利文字的最佳策略相反
- 反复编码会累积损失(世代损失)
我的补充:两条可执行的推论。一是别对已经有损压缩过的图再压一次,每次都掉质量——保留无损原图作为唯一真源,所有派生版本从它生成。二是含文字的截图用有损格式会让字发虚(色度子采样的直接后果),这类图要么用无损 WebP/PNG,要么把质量拉到很高。
47. Image formats: Color models for humans and devices
来源:MDN Blog · developer.mozilla.org 读它解决什么:图片在不同屏幕上颜色不一致,或者 CSS 颜色和图片颜色对不上。
要点
- 色彩模型(RGB、HSL、Lab、OKLab)与色彩空间(sRGB、Display P3)是不同层面的概念
- 广色域屏幕上,未标注色彩空间的图片会被按 sRGB 解释,可能偏色
- CSS 侧现在能表达 P3 色域的颜色,与图片的色彩管理需要对齐
我的补充:实际会遇到的问题:设计稿在 P3 屏幕上做的,导出成 sRGB 图片后颜色变灰。解决办法是导出时嵌入色彩配置文件,并在 CSS 里用 color(display-p3 ...) 表达对应颜色。多数博客场景不必深究——统一用 sRGB 就好,一致性比鲜艳更重要。
二、TypeScript:大版本的实际影响
48. Announcing TypeScript 7.0
来源:TypeScript DevBlog · devblogs.microsoft.com 读它解决什么:判断升到 7.0 要付出什么、得到什么。
要点
- 大版本通常同时带来新语法能力与破坏性的类型检查收紧,后者是升级成本的主要来源
- 升级前应通读 breaking changes,尤其涉及库类型声明的部分
- 编译器性能改进对大型项目的开发体验影响很实际
我的补充:TypeScript 升级的标准流程:先在 CI 里加一个允许失败的 tsc --noEmit 任务跑新版本,看错误量级再决定节奏。错误集中在第三方 @types 包时,往往等上游更新比自己改快。另外别忘了 skipLibCheck: true 能挡掉大量第三方声明冲突,虽然不够严格但在迁移期很实用。
49. Announcing TypeScript 7.0 RC
来源:TypeScript DevBlog · devblogs.microsoft.com 读它解决什么:正式版之前想提前验证兼容性。
要点
- RC 阶段 API 与行为已基本冻结,是做兼容性验证的合适时机
- 此时反馈的问题还有机会在正式版修掉
- 用 RC 跑一遍自己的项目,成本低于正式版发布后被动应对
我的补充:建议做法:pnpm add -D typescript@rc 在一个分支上跑 tsc --noEmit,把错误分类。只需要跑类型检查,不需要真的用它构建,风险极低。开源项目更应该做这件事——你的使用者会比你更早遇到问题。
50. Announcing TypeScript 7.0 Beta
来源:TypeScript DevBlog · devblogs.microsoft.com 读它解决什么:最早期了解下个大版本的方向。
要点
- Beta 阶段公布主要特性方向,细节可能仍变
- 适合了解趋势,不适合据此改代码
- 关注点应放在"哪些现有写法会被判为错误"
我的补充:Beta 阶段最值得看的是弃用与收紧列表,因为那决定你的迁移工作量。新语法糖可以等正式版再学。一个长期有效的做法是:tsconfig.json 里尽量开严格模式(strict: true),严格模式下写的代码在版本升级时基本不会因为检查收紧而报错——问题总是出在宽松配置的项目上。
51. Announcing TypeScript 6.0
来源:TypeScript DevBlog · devblogs.microsoft.com 读它解决什么:了解 6.0 这一代引入的能力,很多项目仍在这个版本上。
要点
- 版本公告是了解"某个语法从哪个版本开始可用"的权威来源
- 对库作者尤其重要:
package.json里的typesVersions与最低支持版本要对应 - 类型系统能力的增强(推断、模板字面量类型)通常无破坏性
我的补充:查"这个特性要求哪个 TS 版本"最快的方式就是翻版本公告。库作者请在 README 明确写出最低 TypeScript 版本——用了新语法但没声明,使用者会遇到莫名的类型报错。另外 tsconfig 的 target 和 TS 版本是两件事,别混。
52. Trustworthy JavaScript for the Open Web
来源:Mozilla Hacks · hacks.mozilla.org 读它解决什么:用户凭什么相信网页里跑的 JS 没被篡改。
要点
- 网页应用的代码每次访问都从服务器取,无法像原生应用那样一次性审计
- 这对端到端加密类应用是根本问题:服务器可以随时下发一个偷密钥的版本
- 解决方向涉及代码完整性验证与可复现构建
我的补充:这解释了为什么安全审计人员对"网页版端到端加密"持保留态度。对普通项目的可执行部分是子资源完整性(SRI):引用第三方 CDN 脚本时加 integrity="sha384-..." 与 crossorigin,CDN 被投毒时浏览器会拒绝执行。这是很便宜的防护,但用的人不多。更彻底的做法是把第三方脚本自托管。
53. Securing APIs: Express rate limit and slow down
来源:MDN Blog · developer.mozilla.org 读它解决什么:接口被刷(暴力破解、爬取、滥用),需要限流。
要点
- 限流(rate limit)直接拒绝超额请求,减速(slow down)逐步增加延迟,两者可组合
- 关键设计是限流的键:按 IP 会误伤 NAT 后的用户,按用户需要已认证
- 反向代理后面必须正确读取真实 IP,否则所有请求看起来来自同一个地址
我的补充:trust proxy 配错是最常见的失效原因——Express 默认不信任代理头,在 Nginx/Cloudflare 后面拿到的 req.ip 是代理的 IP,于是全站共用一个限流配额,一个人就能把所有人锁死。Cloudflare 后面应读 CF-Connecting-IP。登录接口的限流应该按"IP + 账号"双维度,只按账号会被用来锁死别人的账号(这本身是一种攻击)。
三、引擎与运行时
54. How we made JSON.stringify more than twice as fast
来源:V8 Blog · v8.dev 读它解决什么:了解引擎层面的优化思路,顺便知道 JSON.stringify 的代价在哪。
要点
JSON.stringify在服务端与前端都是热路径(日志、API 响应、状态序列化)- 优化通常来自减少中间字符串分配与针对常见形状(shape)走快路径
- 这类改进无需改代码即可获得,但前提是升级引擎版本
我的补充:实用推论:序列化大对象是同步阻塞操作,主线程上 stringify 几十 MB 的数据会明显卡顿,Node 里会阻塞事件循环。真有大数据要序列化,考虑流式(JSONStream)或放进 Worker。另外别在渲染循环里 stringify 对象做比较(常见的"深比较"偷懒写法),代价比想象中高。
55. Giving V8 a Heads-Up: Faster JavaScript Startup with Explicit Compile Hints
来源:V8 Blog · v8.dev 读它解决什么:首屏 JS 解析编译耗时长,想让引擎优先编译关键函数。
要点
- 引擎默认惰性编译(只在函数首次调用时编译),这减少启动开销但让首次调用有延迟
- 显式编译提示允许标记"这些函数马上会用到,请提前编译"
- 收益在大型应用的启动阶段最明显
我的补充:这类优化的前提是你的瓶颈真的在脚本编译——先用 DevTools Performance 面板看 Script Evaluation 的耗时占比,别凭猜测优化。多数站点的首屏瓶颈其实是网络和渲染,不是编译。真要减少编译开销,代码分割和少发 JS 的收益远大于编译提示。
56. Land ahoy: leaving the Sea of Nodes
来源:V8 Blog · v8.dev 读它解决什么:了解 JIT 编译器内部表示的演进,理解性能为什么会变。
要点
- 编译器中间表示(IR)的选择影响优化能力与编译速度,两者常有冲突
- 换 IR 这类底层重构对开发者是透明的,但会改变性能特征
- 这解释了为什么同一段代码在不同引擎版本上性能会有差异
我的补充:对日常开发的启示是别做基于特定引擎版本的微优化——那些"这样写比那样写快"的经验,往往在引擎更新后失效甚至反转。该做的是算法层面和架构层面的优化,那些是稳定的。真要做性能对比就在目标环境实测,别信几年前的 benchmark 文章。
57. Speculative Optimizations for WebAssembly using Deopts and Inlining
来源:V8 Blog · v8.dev 读它解决什么:了解 Wasm 的性能特征与优化机制。
要点
- Wasm 传统上是提前编译、性能可预测,引入推测优化后能获得类似 JS JIT 的动态收益
- 推测优化需要"去优化"(deopt)机制兜底:假设不成立时回退
- 内联跨模块调用能消除边界开销
我的补充:这打破了"Wasm 性能恒定"的旧认知——现在它也有预热期了。对选型的影响:如果你选 Wasm 是为了"性能可预测"(比如实时音频处理),要重新验证。多数场景这是好事,因为峰值性能更高。实测仍是唯一可靠依据。
58. Evolving the Node.js Release Schedule
来源:Node.js 官方博客 · nodejs.org 读它解决什么:搞清 Node 的版本节奏,决定生产该用哪个版本。
要点
- Node 有 Current 与 LTS 两条线:偶数版本会进入 LTS(长期支持),奇数版本不会
- LTS 有 Active 与 Maintenance 两阶段,后者只修安全问题
- 发布节奏调整会影响升级规划
我的补充:铁律是生产环境只用偶数号 LTS。查支持周期看 nodejs.org/en/about/previous-releases。CI 里建议同时跑当前 LTS 和下一个 LTS,能提前发现兼容问题。另外注意 package.json 的 engines 字段要写实际测过的范围,写 >=18 但从没在 18 上跑过是在骗使用者。
59. Node.js 24.19.0 (LTS)
来源:Node.js 官方博客 · nodejs.org 读它解决什么:查看具体某个 LTS 版本的变更内容。
要点
- 版本发布说明是判断"该不该升这个小版本"的一手依据,尤其含安全修复时
- 语义化版本在 Node 这里执行得比较严格:LTS 的小版本升级通常安全
- 关注
SEMVER-MINOR标记的新增能力
我的补充:这正好是我本机在用的版本(node -v 显示 v24.19.0)。小版本更新应该跟得比较勤,尤其带安全修复的——Node 的安全公告通常同时放出多个受影响分支的修复版。用 nvm 或 fnm 管多版本,别用系统包管理器装 Node,那通常版本很旧且升级麻烦。
60. Node.js Security Releases (July 2026)
来源:Node.js 官方博客 · nodejs.org 读它解决什么:确认自己的 Node 版本是否受某次安全公告影响。
要点
- 安全发布公告列出受影响版本范围、漏洞影响与修复版本号
- 需要判断的是你的使用方式是否触发该漏洞,而不只是版本号匹配
- 多个 LTS 分支通常同时放出修复版
我的补充:订阅 Node 的安全公告(RSS 或邮件列表)比等扫描器报警及时。判断优先级看攻击面:如果漏洞需要处理不可信输入才能触发,而你的服务只跑内部任务,紧急程度就低。但基础镜像该定期重建——很多人的容器里跑着一年前的 Node。CI 里加 npm audit 或 pnpm audit 能兜住依赖侧的问题。
本卷小结
三条:
- 截断字符串用
Intl.Segmenter的 grapheme 粒度。'👨👩👧'.length是 8,用slice会切出乱码方块。 - Express 在代理后面必须配
trust proxy。不配则全站共用一个限流配额,一个人能锁死所有人。 - 生产只用偶数号 Node LTS,并且别用系统包管理器装 Node。
