JS 挖过的坑,你知道多少?

小麦2025年08月26日1817 字

聊一聊 JS 历史上挖过的坑,以及新标准是怎么填坑的。

欢迎来到前端杂谈,我是小麦。

今天来聊一个喜闻乐见的话题:复盘一下历史上 JS 挖下了哪些坑,然后又是怎么填回去的?

那么首先就是双等号,我们知道 JS 是弱类型语言,早期 JS 的设计也偏向 “易用” 而非 “精确”,然而却埋下了很多问题。

比如这里的数组和非数组是相同的,数字 0 和空字符串是相同的,false 和字符 0 也是相同的,背后的原因是隐式转换规则在起作用。

然而隐性转换规则本身也是非常复杂的,容易让人摸不着头脑。

为了解决这个问题,ES3(1999)标准提出了严格相等运算符,直接把隐式类型转换禁掉了。

于是这张图就变成了这样,看着舒服多了。

老一代的前端程序员应该有不少是从用 var 开始的。

var 的坑之多,甚至让它成为了面试必考题之一,这里我举三个案例来说明。

首先就是 var 的声明提升,在声明前使用了变量 a,理论上应该报引用错误 ReferenceError 对吗?

但实际上 JS “优雅地” 帮你降级成了 undefined,保证程序能 “正常” 运行。

这段代码的等效写法是右边这样的,变量声明被隐式置顶(提升),但是赋值语句并没有,而从不会报错。

var 的另一个坑是没有块级作用域,导致通过 if/for/while 等创建的块无法约束变量,容易覆盖掉外面定义的同名变量导致 bug。

下面这个循环闭包也是经典中的经典,猜猜三次循环的打印结果是什么?

嗯,老司机一定异口同声都是 3,新司机可以暂停想想为什么?

为了填上 var 这个天坑,ES6(2015)提出了我们熟悉的 let 和 const,取消了声明提升并引入块级作用域,我相信在座的各位已经没有人还在用 var 了吧。

这个问题想必很多朋友都不陌生,

在 JS 中,浮点数之间的数学运算可能出现精度问题。

我之前做过一期视频深入分析过,感兴趣的可以看看。

为了填上这个坑,JS 现有一个尚处于 Stage 1 的 Decimal 提案正在推进,

它引入一个叫做 Decimal 的全局对象来处理浮点数。

如果用字面量(Literal)的写法,看起来会简单些。不过目前 Decimal 提案还处于早期,未来写法可能发生变化。

现阶段一般会使用 decimal.js 这个三方库来规避精度问题。

new Array 大家可能用的较少,但是一用一个不吱声。

猜猜这个表达式的值是多少?

如果在浏览器控制台里看,它是这样显示的。

哈?这个 empty x 3 是个什么玩意儿?

这就要提到稀疏数组了,稀疏数组是指包含空槽(empty slot)的数组,空槽没有被赋值。

例如,如果给 arr 赋值为 1、空格、3 ,那么这个稀疏数组的第二个元素就是 empty,而总长度依然是 3。

回到这个例子,使用 new Array 构建数组时,传入的 3 仅仅是设置了一下 length 属性,并没有实际填充值,导致 map 映射没有执行。

这个问题其实很好解决,紧接着调用一次 fill 函数,或者使用 Array.from 的第二个初始化函数即可。

数组的一些原生方法不太合群,表现出差异化行为,时常小坑开发者一把。

我们知道无论是 map、filter、forEach、reduce 还是 slice,都不会对原数组造成影响。

但当你用到 sort、reverse 或是 splice 的时候就要多长个心眼儿,原先的数组内容会被原地修改。

这种设计上的不一致给开发者带来一些困扰,也容易产生 bug。

因此 ES2023 添加了新的 toSorted、toReserved 和 toSpliced 方法来填上这个坑。

Date 这个对象想必大家不陌生,原生的 Date 存在很多问题。

第一个就是月份问题。

猜猜这段代码代表的 UTC 时间是多少呢?

如果是 2025 年 1 月 1 日那就大错特错了。

实际时间是 2025 年 1 月 31 日,如果以北京时间(UTC+8)计算,那么刚好是 2025 年 2 月 1 日的零点。你看,整整差了一个月。

在原生 Date 中,月数的范围是 0 到 11,0 代表 1 月,1 代表 2 月,以此类推。

第二个坑是字符串解析实现有差异,特别是在非标格式下。

导致的问题就是:人看着都一样,但是 JS 认为不一样。

第三个坑是日期计算溢出问题。

考虑这段代码,将月数加一之后,最后这个 date 的月份值是多少呢?

你应该猜到是 2 而不是 1,因为 2 月没有 31 号,直接滚到 3 月去了。

如果你用日期处理库,比如 dayjs 进行同样的计算,就不会出现此问题。

第四个坑是越界滚动,当月份或日期不在有效范围内时,会自发滚动。

比如这个例子中,把日期数设为 -1 会从 31 开始往前回退两天变成 29。

很难说这是一个 feature 还是一个 bug,总之是个神坑。

Date 病入膏肓,除了使用第三方的日期库外,还能治吗?有的,兄弟,有的。

新的日期处理方法 Temporal 即将进入标准,它的目标就是取代 Date,

同时来感受一下新的日期格式规范的复杂性。

当然除了以上说到的这些神坑外,还有很多。比如:this 指向问题、typeof null 是 object、hasOwnProperty、NaN 判断问题、parseInt 的暗坑、for…in、arguments 默认值缺失,这些可谓是害苦了当年的前端程序员。

不过随着标准的进步,这些问题已大有改善。

现在的 JavaScript 已经走向成熟,后起之秀 TypeScript 也将前端程序的健壮性提升到了新的高度。

我们既要了解以前满目疮痍的 JavaScript,也要以发展的眼光来看待它,相信未来的 JavaScript 生态会变得更好。

我是小麦,一位热爱技术和分享的软件工程师,我们下期再见。

本文为「捣鼓键盘的小麦」原创文章,转载请联系微信:Micoozlee

评论

你需要先登录才能发表评论
Made by 捣鼓键盘的小麦 / © 2025 Front Talk 版权所有