查看原文
其他

JavaScript 奇葩行为大赏

CUGGZ 前端充电宝
2024-08-31

本文来分享一些 JavaScript 中离谱的设计,这些设计日常开发遇到的概率可能比较小,但面试可能会问到噢!

parseInt(0.0000005)

答案:5

parseInt(0.5); // -> 0
parseInt(0.05); // -> 0
parseInt(0.005); // -> 0
parseInt(0.0005); // -> 0
parseInt(0.00005); // -> 0
parseInt(0.000005); // -> 0
parseInt(0.0000005); // -> 5

parseInt 函数将其第一个参数转换为字符串(如果它还不是字符串),然后再转换为数字。当将 0.0000005 转换为字符串时,会得到以下结果:

String(0.0000005); // -> "5e-7"

然后 parseInt 函数只取该字符串的第一个字符,即 5,并将其解析为一个数字。

[] == ![]

答案:true

[] == ![] 之所以返回 true,是因为比较过程中发生了隐式的类型转换。下面来逐步解析:

  1. [] 是一个空数组,它是真值。![]false,因为当将空数组强制转换为布尔值时,它变为 true,然后被否定为 false。因此,比较变成了 [] == false

  2. 当比较不同类型时,JavaScript 将尝试将一个或两个值强制转换为相同类型。在这种情况下,它将尝试将数组强制转换为原始值。

  3. 一个空数组,当被强制转换为原始值时,变成了一个空字符串 ""。因此,表达式 [] == false 实际上变成了 "" == false

  4. 现在,JavaScript 尝试将布尔值 false 转换为数字,即 0,表达式就变成了 "" == 0

  5. 根据 JavaScript 的规则,当比较一个字符串和一个数字时,字符串将被强制转换为数字。因此,"" 被强制转换为数字后变成了 0。这时比较的就是 0 == 0,结果是 true

NaN === NaN

答案:false

在 JavaScript 中,NaN(Not a Number)是一个特殊的值,表示一个非数字的值。然而,当使用 ===(严格相等运算符)来比较 NaN 时,会出现一个特殊的情况:NaN 并不等于 NaN。具体来说,NaN === NaN 的结果是 false,尽管两者都是 NaN。这是因为在 IEEE 754 浮点数标准中,NaN 被定义为不等于任何其他值,包括它自身。

要检查一个值是否是 NaN,通常使用 isNaN() 函数,但请注意,isNaN() 对于非数字类型的参数(如字符串或对象)也可能返回 true,因为它会尝试将这些参数转换为数字。更严格的检查方法是使用 Number.isNaN(),它只有在参数确实是 NaN 时才返回 true。

NaN === NaN // false
isNaN(NaN); // true,但这不是最佳方式
Number.isNaN(NaN); // true,这是更好的方式

[1, 2] + [3, 4]

答案:"1,23,4"

在 JavaScript 中,当尝试使用 + 运算符来连接两个数组,实际上并不会执行数组的拼接或合并。相反,由于 + 运算符在 JavaScript 中既可以用作加法运算符(对于数字),也可以用作字符串连接运算符(对于字符串),因此数组会首先被转换为字符串,然后再进行连接。

数组到字符串的转换是通过调用数组的 toString() 方法实现的,这通常会生成一个由数组元素组成的逗号分隔的字符串。因此,[1, 2] 会被转换为 "1,2",而 [3, 4] 会被转换为 "3,4"。然后,这两个字符串会被 + 运算符连接起来,得到 "1,23,4"。所以,[1, 2] + [3, 4] 的结果是 "1,23,4"

如果想要合并两个数组,应该使用数组的 concat() 方法或扩展运算符如下所示:

  • 使用 concat() 方法:

const result = [1, 2].concat([3, 4]); // [1, 2, 3, 4]
  • 使用扩展运算符:

const result = [...[1, 2], ...[3, 4]]; // [1, 2, 3, 4]

typeof null

答案:object

在 JavaScript 早期版本中,所有值都存储在 32 位的单元中,每个单元包含一个小的 类型标签(1-3 bits) 以及当前要存储的数据。类型标签共有五种类型:

000: object - 数据类型为 对象。
1: int - 数据类型为 有符号整数。
010: double - 数据类型为 双精度的浮点数。
100: string - 数据类型为 字符串。
110: boolean - 数据类型为 布尔值。

null 的值是机器码 NULL 指针(指针值是000),也就是说null的类型标签也是 000,和object的类型标签一样,所以会被判定为object

try...finally

答案:2

(() => {
try {
return 1;
} finally {
return 2;
}
})();

在JavaScript中,当在一个函数(包括箭头函数)的try块和finally块中都有return语句时,finally块中的return语句会覆盖try块中的return语句。这是因为finally块总是会被执行,无论try块中的代码是否成功执行,或者是否抛出了异常。而且,如果finally块中有return语句,那么这个return语句将决定整个函数的返回值。

0.14 * 100

答案:14.000000000000002

0.13 * 100 // 13
0.14 * 100 // 14.000000000000002
0.15 * 100 // 15
0.16 * 100 // 16

在JavaScript中,所有的数字都是以 64 位浮点数形式存储的,即使它们被声明为整数。由于二进制无法精确表示所有的十进制小数,因此在进行浮点数运算时,可能会出现精度问题。由于在二进制浮点数表示中,0.14 不能精确表示,因此在进行乘法运算时会出现微小的舍入误差。一个经典的问题就是 0.1 + 0.2 不等于 0.3。这两个问题出现的原因是一样的。

0.1 + 0.2 === 0.3 // false
0.1 + 0.5 === 0.6 // true

为了处理这种精度问题,可以使用Number.EPSILONMath.roundtoFixed等方法来比较浮点数或将其格式化为固定小数位数。如果需要精确计算,并且不能容忍这种舍入误差,可以使用特殊的库,如decimal.jsbignumber.js,它们提供了高精度的十进制数运算。

1.toString()

答案:报错

const num = 1;
num.toString() // 1
1.toString(); // Uncaught SyntaxError: Invalid or unexpected token
1..toString(); // 1

在 JavaScript 中,1.toString() 会导致一个语法错误,因为点号(.)在这里被解析为浮点数的一部分,但紧接着并没有另一个数字来形成有效的浮点数字面量,所以解析器会抛出一个 Uncaught SyntaxError: Invalid or unexpected token 错误。

然而,当写 1..toString() 时,情况就不同了。这里有两个点号,但第一个点号实际上并不是浮点数的一部分。这是因为 JavaScript 的解析器在遇到连续的点号时会将它们视为一种特殊的语法结构,即第一个点号被视为数字 1 的结尾(尽管在这里它并没有实际意义,因为 1 已经是完整的数字),而第二个点号则作为访问对象属性的操作符。

因此,1..toString() 实际上是这样被解析的:

  1. 数字 1 被解析为一个完整的数字字面量。

  2. 由于紧接着有一个点号,但它并没有跟随另一个数字来形成浮点数,所以它被解释为对象属性的访问操作符。

  3. 因为 1 在 JavaScript 中是一个原始值,它本身并没有 .toString() 方法,但是在这里,由于点号操作符的存在,JavaScript 会尝试将 1 转换为一个 Number 对象(这是一个称为装箱或自动封装的过程)。

  4. 一旦 1 被转换为 Number 对象,就可以调用它的 .toString() 方法了。

所以,1..toString() 最终会返回字符串 "1",尽管这种写法在实际编程中并不常见,因为它可能会引起混淆。更常见的做法是直接对数字变量使用 .toString() 方法,也就是上面的第一种写法。

Math.max() < Math.min()

答案:true

Math.max() < Math.min() // true
Math.max() // -Infinity
Math.min() // Infinity

在标准的 JavaScript 环境中,Math.max() 在没有参数时应该返回 -Infinity,而 Math.min() 在没有参数时应该返回 Infinity。但是,由于 Infinity 总是大于 -Infinity,所以 Math.max() < Math.min() 返回 true。

9007199254740992 === 9007199254740993

答案:trueJavaScript 的 Number 类型是基于 IEEE 754 标准 (也称为 64 位浮点数)实现的,这意味着它有一些限制,特别是关于可以精确表示的数字的范围和精度。在 IEEE 754 标准中,最大的安全整数(即可以精确表示的最大整数)是 Number.MAX_SAFE_INTEGER,其值为 9007199254740991(2 的 53 次方减 1)。

当尝试使用大于 Number.MAX_SAFE_INTEGER 的整数时,JavaScript 会尝试将其存储为一个近似的浮点数,这可能会导致精度损失。在这个例子中,90071992547409929007199254740993 都会被转换为近似的浮点数,但由于精度限制,这两个数字可能会表示为相同的浮点数值。

因此,如果需要在 JavaScript 中表示大数字时,建议使用字符串来存储大数,以避免精度丢失。

往期推荐

前端程序员最讨厌的是什么?

前端构建工具大盘点

微软Edge浏览器放弃 React,性能大幅提升!

最适合程序员的编程字体,好看、优雅!

尤雨溪:这个前端经典轮子值得去造!

CSS 3 这么多年,CSS 4、CSS 5 终于要来了!

前端跨平台开发框架大盘点

2024 年前端框架大更新

继续滑动看下一个
前端充电宝
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存