JavaScript 冷知识合集:这些特性你都知道吗?
JavaScript 作为一门动态的脚本语言,有着无穷的灵活性和意想不到的特性。即使你已经是一名资深开发者,也可能会对某些冷知识感到意外。本文整理了一些 JavaScript 冷知识,既有实用性,也有趣味性,希望你看完之后可以在项目中实践或者用来和同事“炫技”。
1. [] == ![]
的结果为什么是 true
?
console.log([] == ![]); // true
解析:
这个问题乍看起来毫无道理,但实际上是 JavaScript 的类型转换规则导致的。
-
![]
转换为布尔值:- 空数组是一个对象,它的布尔值为
true
,但![]
是false
。
- 空数组是一个对象,它的布尔值为
-
类型转换规则:
==
会将两边的操作数转换为相同类型。[]
转为字符串,变成""
。![]
是false
,转换为数字为0
。
-
最终比较:
"" == 0
为true
,因为空字符串会被转为数字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
实用场景:
快速将字符串转为数字,替代 parseInt
或 Number()
。
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 是一门让人又爱又恨的语言。它的灵活性既给我们带来了便利,也让它充满了奇怪的冷知识。希望这些冷知识能让你在开发中找到新的乐趣!如果你也有发现其他有趣的知识点,欢迎在评论区补充!