JavaScript 冷知识合集:这些特性你都知道吗?

2 阅读3分钟

JavaScript 冷知识合集:这些特性你都知道吗?

JavaScript 作为一门动态的脚本语言,有着无穷的灵活性和意想不到的特性。即使你已经是一名资深开发者,也可能会对某些冷知识感到意外。本文整理了一些 JavaScript 冷知识,既有实用性,也有趣味性,希望你看完之后可以在项目中实践或者用来和同事“炫技”。


1. [] == ![] 的结果为什么是 true

console.log([] == ![]); // true
解析:

这个问题乍看起来毫无道理,但实际上是 JavaScript 的类型转换规则导致的。

  1. ![] 转换为布尔值

    • 空数组是一个对象,它的布尔值为 true,但 ![]false
  2. 类型转换规则

    • == 会将两边的操作数转换为相同类型。
    • [] 转为字符串,变成 ""
    • ![]false,转换为数字为 0
  3. 最终比较

    • "" == 0true,因为空字符串会被转为数字 0

2. 0.1 + 0.2 === 0.3 为什么是 false

console.log(0.1 + 0.2 === 0.3); // false
解析:

这是由于 JavaScript 使用 IEEE 754 双精度浮点数表示数字。这种表示方式无法精确存储某些十进制小数,导致计算时产生了微小的误差。

console.log(0.1 + 0.2); // 0.30000000000000004
解决方案:

通过 Number.EPSILON 校正误差:

console.log(Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON); // true

3. Object.is()=== 的微妙区别

console.log(Object.is(NaN, NaN)); // true
console.log(NaN === NaN);         // false
解析:

Object.is() 用来比较两个值是否完全相同,和 === 很相似,但处理特殊值(如 NaN-0)时有区别:

  • NaN 比较

    • ===NaN 不等于自身。
    • Object.is()NaN 等于自身。
  • -0+0

    console.log(Object.is(-0, +0)); // false
    console.log(-0 === +0);         // true
    
实用场景:

当你需要精确比较特殊值时,Object.is() 是更可靠的选择。


4. 小数点后直接调用方法

你知道可以直接对数字调用方法吗?不过写法需要小心。

console.log(5..toString());   // "5"
console.log(5 .toString());  // "5"
console.log((5).toString()); // "5"
解析:
  • 第一个点是小数点,第二个点是对象访问符。
  • 为了避免歧义,括号或者额外的空格可以让代码更清晰。

5. JavaScript 的 + 是个多面手

+ 在 JavaScript 中不仅仅是一个加法运算符,它还能用于类型转换:

console.log(+"123");    // 123 (字符串变为数字)
console.log(+true);     // 1
console.log(+false);    // 0
console.log(+null);     // 0
console.log(+undefined);// NaN
实用场景:

快速将字符串转为数字,替代 parseIntNumber()


6. 函数参数的“魔术”变化

JavaScript 的函数参数对象 arguments 并不是一个真正的数组,它是一个“类数组对象”。不过,在严格模式下,它的行为发生了变化。

function test(a, b) {
  a = 10;
  console.log(arguments[0]); // 非严格模式:10;严格模式:1
}

test(1, 2);
解析:
  • 在非严格模式下,修改参数变量会同步更新 arguments 对象。
  • 在严格模式下,参数和 arguments 之间不再同步。

7. 函数默认参数也有作用域

函数的默认参数值可以访问之前的参数:

function greet(name, message = `Hello, ${name}`) {
  return message;
}

console.log(greet("Alice")); // Hello, Alice
解析:

默认参数在定义时会创建作用域链,后面的参数可以引用前面的参数。


8. “冻结”的对象仍可修改内部属性

Object.freeze() 冻结的是对象本身,而非其内部的属性。

const obj = { nested: { value: 1 } };
Object.freeze(obj);

obj.nested.value = 42;
console.log(obj.nested.value); // 42
解决方案:

如果需要真正冻结,可以递归处理:

function deepFreeze(obj) {
  Object.freeze(obj);
  Object.keys(obj).forEach((key) => {
    if (typeof obj[key] === "object") {
      deepFreeze(obj[key]);
    }
  });
}

const obj2 = { nested: { value: 1 } };
deepFreeze(obj2);
obj2.nested.value = 42; // 不会修改

9. setTimeout 的最短延迟时间是 4ms

即使你设置了 setTimeout 的延迟为 0ms,浏览器仍然会将其最短延迟设置为 4ms

setTimeout(() => console.log("Hello"), 0);
console.log("World");
// 输出顺序:World -> Hello
解析:

JavaScript 的事件循环机制规定,setTimeout 的回调最早在下一次事件循环执行,而非立即执行。


10. 使用正则表达式判断素数

虽然看起来不可思议,但你可以用正则表达式判断一个数字是否是素数。

function isPrime(number) {
  return !/^1?$|^(11+?)\1+$/.test("1".repeat(number));
}

console.log(isPrime(7)); // true
console.log(isPrime(8)); // false
解析:
  • 正则匹配通过重复检测数字是否可以被整除,若不能整除则为素数。

结语

JavaScript 是一门让人又爱又恨的语言。它的灵活性既给我们带来了便利,也让它充满了奇怪的冷知识。希望这些冷知识能让你在开发中找到新的乐趣!如果你也有发现其他有趣的知识点,欢迎在评论区补充!