js中一些奇怪的有趣的玩法

432 阅读12分钟

一、[] + {} 的结果

  1. 空数组 []  被转换为字符串:

    • 当数组与其他类型相加时,JavaScript 会首先将其转换为字符串。空数组 [] 转换为字符串时会变成 ""(空字符串)。
  2. 空对象 {}  被转换为字符串:

    • 在与字符串进行连接时,对象会被转换为字符串。在 JavaScript 中,空对象 {} 转换为字符串时会调用其 toString 方法,结果为 "[object Object]"
  3. 运算过程

    • 因此,[] + {} 实际上变成了 "" + "[object Object]",最终结果就是 "[object Object]"

示例演示:

console.log([] + {}); // 输出: "[object Object]"

二、true + false 的结果

  1. 类型转换

    • true 被转换为数字 1
    • false 被转换为数字 0
  2. 运算过程

    • 因此,true + false 实际上变成了 1 + 0
  3. 计算结果

    • 进行加法运算后,结果为 1

示例演示:

console.log(true + false); // 输出: 1

三、[1, 2, 3].map(parseInt) 的结果

  1. map 方法

    • map 方法会对数组中的每个元素调用指定的函数,并将其返回值组成一个新数组。
    • map 方法会传递三个参数给回调函数:当前元素、当前元素的索引和原数组。
  2. parseInt 函数

    • parseInt 函数有两个参数:要解析的字符串和基数(即进制)。
    • parseInt(string, radix)
  3. 具体计算

    • 当调用 map(parseInt) 时,对于数组 [1, 2, 3],分别会得到:

      • 对于 1parseInt(1, 0) → 1(基数为0,parseInt 会自动判断为基数10)
      • 对于 2parseInt(2, 1) → NaN(基数1是无效的,parseInt 无法解析)
      • 对于 3parseInt(3, 2) → NaN(基数2无法解析数字3)

综上所述,[1, 2, 3].map(parseInt) 的最终结果是:

console.log([1, 2, 3].map(parseInt)); // 输出: [1, NaN, NaN]

三、0.1 + 0.2 的结果

  1. 浮点数表示

    • 在计算机内部,某些十进制数字(如 0.1 和 0.2)无法被精确地转换为二进制浮点数。这种转换的不足会导致运算结果出现微小的误差。
  2. 计算过程

    • 当你执行 0.1 + 0.2 时,实际进行的浮点数运算可能会引入精度错误,从而产生一个看似不合逻辑的结果。
  3. 示例

    • 你可以通过下面的代码查看这一现象:

      console.log(0.1 + 0.2); // 输出: 0.30000000000000004
      

解决办法:

为了避免浮点数精度问题,通常可以采取将计算转换为整数的方式,比如:

const sum = (0.1 * 10 + 0.2 * 10) / 10; // 输出: 0.3
console.log(sum);

或者使用 toFixed 方法来限制小数位数(注意,这会将结果变为字符串):

console.log((0.1 + 0.2).toFixed(1)); // 输出: "0.3"
toFixed 应该注意的地方
  1. 简单易用toFixed() 提供了一种直接的方式来控制数字的小数位数,代码简洁明了。

    const num = 2.34567;
    console.log(num.toFixed(2)); // 输出: "2.35"
    
  2. 格式一致:对于需要特定格式输出的场景,例如财务数据时,toFixed() 可以有效地保证输出的一致性。

  3. 处理浮点数精度:在某些情况下,使用 toFixed() 可以有效减少浮点数运算带来的不确定性。

    const sum = (0.1 + 0.2).toFixed(2);
    console.log(sum); // 输出: "0.30"
    

注意事项:

  1. 返回字符串toFixed() 返回的是一个字符串,而不是数字。如果需要继续进行数学运算,需要将其转换回数字类型。

    const num = 2.34567.toFixed(2); // "2.35"
    const numAsNumber = parseFloat(num); // 转换回数字
    
  2. 可能出现四舍五入toFixed() 会根据四舍五入的规则处理小数部分,可能导致结果超出原始数值的预期。

    console.log((2.345.toFixed(2))); // 输出: "2.35"
    console.log((2.346.toFixed(2))); // 输出: "2.35"(从 2.346 四舍五入到 2.35)
    
  3. 参数范围toFixed() 的参数需要在 0 到 20 之间,超出这个范围会抛出错误。

  4. 浮点数精度问题仍然存在toFixed() 不能解决浮点数本身存在的精度问题,只是提供了一个格式化的方法。

四、前端数据展示后端直接返回大数被浏览器直接截断补0 了怎么处理

在前端开发中,处理大数字时可能会遇到浏览器将其自动转换成科学计数法或直接截断并补零的问题。这种情况通常发生在处理数字时,特别是当数字超出 JavaScript 的安全整数范围(Number.MAX_SAFE_INTEGER,即 2^53 - 1)时。为了解决这个问题,可以考虑以下几种方法:

1. 使用字符串处理(推荐指数五星没有那么多弯弯绕绕)

在后端返回大数字时,可以将其作为字符串返回,而不是作为数字。这可以有效避免任何会导致的数值精度问题。

示例:
// 后端返回
// { bigNumber: "123456789012345678901234567890" }
const data = { bigNumber: "123456789012345678901234567890" };
console.log(data.bigNumber); // 直接处理为字符串

在前端,你可以根据需要显示或处理这个字符串。

2. 使用 BigInt

对于整数,可以考虑使用 BigInt 类型,这是一种可以精确表示任意大小整数的类型。在支持的浏览器中,你可以将数字转换为 BigInt 进行处理。

示例:
const bigNumber = BigInt("123456789012345678901234567890");
console.log(bigNumber); // 123456789012345678901234567890n

注意:BigInt 只能与 BigInt 类型的数字进行运算,不能与普通数字类型混合。

3. 使用第三方库

对于更复杂的情况(如处理小数、运算等),可以使用诸如 Decimal.js 或 Big.js或者 # bignumber.js 等库。这些库提供了高精度的数字运算和处理功能。

示例使用 Decimal.js
// 首先安装库
// npm install decimal.js

const Decimal = require('decimal.js');

const largeNumber1 = new Decimal("12345678901234567890");
const largeNumber2 = new Decimal("0.1");
const result = largeNumber1.plus(largeNumber2);

console.log(result.toString()); // 12345678901234567890.1

4. 后端处理

视情况而定,如果更改后端逻辑可行,可以在返回数据时格式化为字符串,或者仅在必要时将需要计算的部分进行尖端处理,以避免前端遇到精度问题。

5. 格式化显示

如果大数字只是用于展示,可以直接格式化这个字符串,如使用国际化API(Intl.NumberFormat)进行显示,更直观。

示例:
const bigNumber = "123456789012345678901234567890";
const formattedNumber = new Intl.NumberFormat().format(bigNumber);
console.log(formattedNumber); // 1,234,567,890,123,456,789,012,345,678,890

五、[1,'',null,false,undefined,true,34,0,21].fitler(Boolean) 的结果

  1. filter 方法

    • filter 方法创建一个新数组,包含所有通过指定函数测试的元素。在这个例子中,使用 Boolean 函数作为测试函数。
  2. Boolean 函数

    • Boolean 函数会将值转换为布尔值。以下是一些值的真值(truthy)和假值(falsy)情况:

      • true → true
      • false → false
      • 0 → false
      • 1 → true
      • ''(空字符串)→ false
      • null → false
      • undefined → false
      • 34 → true
      • 21 → true
  3. 过滤过程

    • 1 → true,保留
    • '' → false,去除
    • null → false,去除
    • false → false,去除
    • undefined → false,去除
    • true → true,保留
    • 34 → true,保留
    • 0 → false,去除
    • 21 → true,保留

最终结果:

综上所述,经过过滤后,留下的值是 1true34 和 21,所以最终结果是:

console.log([1, '', null, false, undefined, true, 34, 0, 21].filter(Boolean)); 
// 输出: [1, true, 34, 21]

因此,表达式 [1, '', null, false, undefined, true, 34, 0, 21].filter(Boolean) 的结果是 [1, true, 34, 21]

六、底层 valueOf 原理

在 JavaScript 中,valueOf 是一个内置的对象方法,定义在 Object 原型上。它的作用是返回一个对象的原始值。这个方法可以用于所有对象类型,但通常会在某些特定情况下被用到。

作用和用法

  1. 返回原始值

    • valueOf 方法的主要功能是返回对象的原始值。对于大多数对象,valueOf 方法返回的是该对象本身。
    • 对于一些内置对象,如 NumberString 和 Date,这些对象的 valueOf 方法会返回其特定的原始值。
  2. 原型链

    • 自定义对象可以通过重写 valueOf 方法来定义对象在特定上下文中的行为。例如,可以让一个对象在进行数值运算时返回一个特定的值。

示例

1. 基本用法

数组和普通对象的 valueOf

const obj = { a: 1 };
console.log(obj.valueOf()); // 输出: { a: 1 }

const arr = [1, 2, 3];
console.log(arr.valueOf()); // 输出: [1, 2, 3]
2. 内置对象的 valueOf

Number 对象

const num = new Number(42);
console.log(num.valueOf()); // 输出: 42

String 对象

const str = new String("Hello");
console.log(str.valueOf()); // 输出: "Hello"

Date 对象

const date = new Date();
console.log(date.valueOf()); // 输出: 当前时间的时间戳(毫秒数)
3. 自定义对象

你可以通过自定义 valueOf 来改变对象在某些操作中的行为:

class CustomObject {
    constructor(value) {
        this.value = value;
    }

    valueOf() {
        return this.value; // 返回自定义的原始值
    }
}

const customObj = new CustomObject(100);
console.log(customObj + 50); // 输出: 150,调用了 valueOf() 方法

总结

valueOf 方法在 JavaScript 中用于获取对象的原始值。虽然在许多情况下不需要直接调用它,但在涉及类型转换、相加等操作时,JavaScript 会自动调用(自动调用很关键)对象的 valueOf 方法,以决定如何处理对象。这使得开发者可以通过重写 valueOf 来定制对象在不同上下文中的行为,从而更灵活地管理对象的表现。

七、0+null+undefined+[]+[[]]+{} + [] + NaN 的结果

  1. 0 + null

    • null 被转换为数字 0,因此 0 + null 的结果是 0 + 0 = 0
  2. 0 + undefined

    • undefined 在数学运算中被转换为 NaN,所以 0 + undefined 的结果为 0 + NaN = NaN
  3. NaN + []

    • 空数组 [] 转换为字符串时结果是 ""。因此,NaN + [] 变为 NaN + "",结果仍然是 NaN
  4. NaN + [[]]

    • [[ ]] 是一个包含一个空数组的数组。在转换为字符串时,它也会被转换为 ""。因此,NaN + [[]] 变为 NaN + "",结果仍为 NaN
  5. NaN + {}

    • 空对象 {} 在加法运算中调用其 toString 方法,返回 "[object Object]"。因此,NaN + {} 变为 NaN + "[object Object]"。此时 NaN 参与运算,结果依然是 NaN
  6. NaN + []

    • 再次遇到空数组 [],仍然会转换为 ""。所以 NaN + [] 变为 NaN + "",结果还是 NaN
  7. NaN + NaN

    • 最后,在表达式的末尾还有 NaN。因此,将结果 NaN 与 NaN 相加,仍然得到 NaN

最终结果

综合以上步骤,整个表达式 0 + null + undefined + [] + [[]] + {} + [] + NaN 的最终结果为 NaN

示例代码

console.log(0 + null + undefined + [] + [[]] + {} + [] + NaN); // 输出: NaN

八、[[]] + [][] + [] + {} + 0 + '0' 的结果

  1. [[]] :

    • 这是一个包含空数组的数组,表示 "[[]]" 当被转换为字符串时。
  2. [][] :

    • 这是一个对二维数组的访问,[][] 可以被解释为访问一个数组的元素,但在没有具体定义数组的情况下,这里会导致 undefined。然后,undefined 被转换为字符串时,会得到 ""(空字符串)。
  3. [[]] + [][] :

    • 在这里,我们有 [[]] + []。从上面我们知道 [][] 变成了 ""(空字符串)。所以这个部分转换成了:

      • [[]].toString() 结果是 "",加上 "",所以结果是 "[object Object]"
  4. [object Object] + [] :

    • 在这个部分,空数组 [] 转换为 "",所以 "[object Object]" + [] 结果仍然是 "[object Object]"
  5. [object Object] + {} :

    • 空对象 {} 的默认转换结果是"[object Object]"
    • 因此,这一部分转换成了 "[object Object]" + "[object Object]",结果还是 "[object Object]"
  6. "[object Object]" + 0:

    • 在这里,0 被转换为字符串 "",所以 "[object Object]" + 0 依然得到 "[object Object]0"
  7. "[object Object]0" + '0' :

    • 最后,两个字符串相加,所以 "[object Object]0" + '0' 结果是 "[object Object]00"

最终结果

总的来说,整个表达式 [[]] + [][] + [] + {} + 0 + '0' 的最终结果是:

console.log([[]] + [][] + [] + {} + 0 + '0'); // 输出: "[object Object]00"

九、js中的 ++i 和 i++的区别

1. 前缀自增(++i

  • 操作符:前缀自增运算符。

  • 行为:首先将 i 的值增加 1,然后返回增加后的值。

  • 使用场景

    • 在表达式中,使用 ++i 可以直接使用自增后的值。
示例:
let i = 5;
let result = ++i; // i 变为 6,result 也为 6
console.log(i);      // 输出: 6
console.log(result); // 输出: 6

2. 后缀自增(i++

  • 操作符:后缀自增运算符。

  • 行为:首先返回 i 的当前值,然后将 i 的值增加 1。

  • 使用场景

    • 在表达式中,使用 i++ 会首先使用自增前的值,而自增是在表达式后发生的。
示例:
let i = 5;
let result = i++; // result 为 5,i 变为 6
console.log(i);      // 输出: 6
console.log(result); // 输出: 5

总结

  • ++i:首先自增,然后返回新的值。
  • i++ :首先返回当前值,然后再自增。

你可以通过以下示例观察它们在使用中的不同:

let a = 1;
let b = ++a; // a 变为 2,b 为 2

let x = 1;
let y = x++; // y 为 1,x 变为 2

console.log(a, b); // 输出: 2, 2
console.log(x, y); // 输出: 2, 1

十、js中隐式转换 & 参与运算后的底层转换逻辑

1. 数字转换

  • 字符串到数字

    • 如果字符串可以被解析为数字,JavaScript 会尝试进行转换。
    console.log("5" - 2); // 输出: 3
    console.log("5" * 2); // 输出: 10
    
  • 布尔值到数字

    • true 转换为 1false 转换为 0
    console.log(true + 1); // 输出: 2
    console.log(false + 1); // 输出: 1
    
  • NaN

    • NaN 参与任何计算时其结果仍然是 NaN

2. 字符串转换

  • 数字到字符串

    • JavaScript 在需要字符串的上下文中会将数字转换为字符串。
    console.log(5 + "10"); // 输出: "510"
    
  • 布尔值到字符串

    • true 转换为 "true"false 转换为 "false"
    console.log(String(true)); // 输出: "true"
    

3. 布尔值转换

  • 以下值被认为是“假” (将转换为 false):

    • 0
    • -0
    • NaN
    • ""(空字符串)
    • null
    • undefined
  • 其余值被转换为 true

4. 对象转换

  • 对象到原始值

    • 当使用对象参与运算时,JavaScript 会调用该对象的 valueOf 方法(优先),如果方法返回的是对象,接着会调用 toString 方法来获取原始值。
    const obj = {
        valueOf: function() {
            return 42;
        }
    };
    console.log(obj + 2); // 输出: 44
    

5. 比较操作

  • 在进行比较时,可能会导致类型转换:

    • 使用 <><=>= 比较时,会进行转换。
    console.log(5 > "4"); // 输出: true
    
  • 使用 ==(相等)时,会进行类型转换。

    • 使用 ===(全等)时,不会进行转换。
    console.log(0 == "0"); // 输出: true
    console.log(0 === "0"); // 输出: false
    

6. 函数调用

  • 当一个值用于函数调用时,如果该值不是函数,JavaScript 会将其转换为函数(通常会引发错误)。

7. 数组与字符串

  • 数组到字符串

    • 当数组被用作字符串时,调用数组的 toString 方法,返回以逗号分隔的字符串。
    console.log([1, 2, 3] + "4"); // 输出: "1,2,34"
    

好了就先整理这么多有趣的js潜藏知识,梳理过程中的代码 如有误请及时指正~ 原创内容,未经允许不得转载!!!