当前位置: 欣欣网 > 码农

JavaScript 奇葩行为大赏

2024-06-07码农

本文来分享一些 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{
    return1;
    }finally{
    return2;
    }
    })();

    在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.EPSILON Math.round toFixed 等方法来比较浮点数或将其格式化为固定小数位数。如果需要精确计算,并且不能容忍这种舍入误差,可以使用特殊的库,如 decimal.js bignumber.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

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

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

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

    往期推荐