文章目录
11.JavaScript 其他值到数字值的转换规则
1. Undefined 类型
undefined
转换为 NaN
。
console.log(Number(undefined)); // NaN
2. Null 类型
null
转换为 0
。
console.log(Number(null)); // 0
3. Boolean 类型
true
转换为 1
,false
转换为 0
。
console.log(Number(true)); // 1
console.log(Number(false)); // 0
4. String 类型
字符串按照 Number()
函数进行转换。
- 如果字符串包含非数字字符,则转换为
NaN
。 - 空字符串转换为
0
。
console.log(Number("42")); // 42
console.log(Number("3.14")); // 3.14
console.log(Number("")); // 0
console.log(Number("hello")); // NaN
console.log(Number("42abc")); // NaN
5. Symbol 类型
Symbol
值不能转换为数字,会抛出 TypeError
。
let sym = Symbol("desc");
// console.log(Number(sym)); // TypeError: Cannot convert a Symbol value to a number
6. 对象到数字的转换
对象(包括数组)会首先被转换为相应的基本类型值,然后再根据基本类型值的转换规则进行强制转换。步骤如下:
- JavaScript 尝试将对象转换为基本类型值。内部会首先检查该对象是否有
valueOf()
方法。 - 如果
valueOf()
存在并返回基本类型值,则使用该值进行强制类型转换。 - 如果没有
valueOf()
方法或其返回值不是基本类型,则使用toString()
方法的返回值进行转换。 - 如果
valueOf()
和toString()
均不返回基本类型值,会产生TypeError
错误。
let obj1 = {
valueOf() {
return 42;
}
};
console.log(Number(obj1)); // 42
let obj2 = {
toString() {
return "3.14";
}
};
console.log(Number(obj2)); // 3.14
let obj3 = {
valueOf() {
return {};
},
toString() {
return {};
}
};
// console.log(Number(obj3)); // TypeError: Cannot convert object to primitive value
7. 数组的转换
数组在转换为数字时,会被首先转换为字符串,然后再根据字符串的转换规则进行转换。如果数组包含多个元素,结果通常为 NaN
,因为转换后的字符串包含逗号分隔的元素。
console.log(Number([1, 2, 3])); // NaN
console.log(Number([42])); // 42
console.log(Number([])); // 0
console.log(Number(["3.14"])); // 3.14
12.JavaScript 其他值到布尔值的转换规则
以下值在转换为布尔值时会被转换为 false
:
undefined
null
false
+0
-0
NaN
""
(空字符串)
console.log(Boolean(undefined)); // false
console.log(Boolean(null)); // false
console.log(Boolean(false)); // false
console.log(Boolean(0)); // false
console.log(Boolean(-0)); // false
console.log(Boolean(NaN)); // false
console.log(Boolean("")); // false
除了上述之外的所有值,在转换为布尔值时都会被转换为 true
。这包括:
- 非空字符串
- 非零数字(包括正数和负数)
- 对象(包括空对象)
- 数组(包括空数组)
- 函数
console.log(Boolean("hello")); // true
console.log(Boolean(42)); // true
console.log(Boolean(-42)); // true
console.log(Boolean({})); // true
console.log(Boolean([])); // true
console.log(Boolean(function(){}));// true
13.JavaScript 中 ||
和 &&
操作符的返回值
逻辑操作符 ||
和 &&
有特定的返回值规则,它们不仅仅返回布尔值,还可能返回操作数本身。规则如下:
1. 逻辑或操作符(||
)
逻辑或操作符 ||
会在找到第一个真值时立即返回该值。如果所有操作数都为假值,则返回最后一个操作数。具体规则如下:
- 对第一个操作数进行条件判断。
- 如果第一个操作数的条件判断结果为
true
,则返回第一个操作数的值。 - 如果第一个操作数的条件判断结果为
false
,则返回第二个操作数的值。
console.log(false || true); // true
console.log(0 || 42); // 42
console.log('' || 'default'); // "default"
console.log(null || 'fallback'); // "fallback"
console.log(undefined || 'ok'); // "ok"
console.log(false || 0 || 'foo'); // "foo"
console.log('' || 0 || NaN); // NaN
2. 逻辑与操作符(&&
)
逻辑与操作符 &&
会在找到第一个假值时立即返回该值。如果所有操作数都为真值,则返回最后一个操作数。具体规则如下:
- 对第一个操作数进行条件判断。
- 如果第一个操作数的条件判断结果为
false
,则返回第一个操作数的值。 - 如果第一个操作数的条件判断结果为
true
,则返回第二个操作数的值。
console.log(true && false); // false
console.log(42 && 0); // 0
console.log('foo' && 'bar'); // "bar"
console.log('hello' && 123); // 123
console.log(true && 'ok'); // "ok"
console.log(1 && 2 && 3); // 3
console.log('' && 'fallback'); // ""
console.log(null && 'should not reach'); // null
总结
||
操作符:返回第一个真值,或者在所有操作数均为假值时返回最后一个操作数。&&
操作符:返回第一个假值,或者在所有操作数均为真值时返回最后一个操作数。
14.==
(双等号)与 ===
(三等号)的区别
1. ==
(双等号)
==
是宽松相等性比较操作符,会在比较两个值时进行类型转换。如果操作数的类型不同,会先将它们转换为相同类型再进行比较。规则如下:
- 类型不同:会进行类型转换。
null
和undefined
:null
和undefined
视为相等。- 布尔值:布尔值会转换为数字再进行比较。
- 字符串和数字:字符串和数字比较时,会将字符串转换为数字。
- 对象与原始类型:对象与原始类型比较时,会将对象转换为原始类型。
示例:
console.log(2 == '2'); // true
console.log(null == undefined); // true
console.log(true == 1); // true
console.log(false == 0); // true
console.log('' == 0); // true
console.log([1, 2] == '1,2'); // true
2. ===
(三等号)
===
是严格相等性比较操作符,不会进行类型转换。如果两个值的类型不同,直接返回 false
。规则如下:
- 类型不同:返回
false
。 - 类型相同:再进行值的比较。
示例:
console.log(2 === '2'); // false
console.log(null === undefined); // false
console.log(true === 1); // false
console.log(false === 0); // false
console.log('' === 0); // false
console.log([1, 2] === '1,2'); // false
3. 性能与可预测性
-
性能:由于
===
不需要进行类型转换,通常比==
的执行速度更快。但在现代 JavaScript 引擎中,这种性能差异微不足道。 -
可预测性:
===
的行为更加可预测,不会出现一些令人困惑的结果。例如:console.log(0 == false); // true console.log(0 === false); // false console.log(null == undefined); // true console.log(null === undefined); // false
4. 特殊值:NaN
NaN
是唯一一个不等于自身的值,无论是用 ==
还是 ===
:
console.log(NaN == NaN); // false
console.log(NaN === NaN); // false
如果需要检查一个值是否为 NaN
,应该使用 isNaN()
函数或 Number.isNaN()
方法。
5. 建议
建议在代码中尽量使用 ===
,这样可以避免很多潜在的错误,使代码更加健壮和可维护。同时,理解 ==
的行为也很重要,因为你可能会在一些旧代码或特定场景中遇到它。
15.Object.is()
与 ==
和 ===
的区别
Object.is()
在大多数情况下与 ===
的行为相同,但在一些特殊情况(如 -0
和 +0
,以及 NaN
)上有所不同。规则如下:
- 类型不同:返回
false
。 - 类型相同:再进行值的比较。
- 特殊情况:
-0
和+0
不相等。- 两个
NaN
是相等的。
示例:
console.log(Object.is(2, '2')); // false
console.log(Object.is(null, undefined)); // false
console.log(Object.is(true, 1)); // false
console.log(Object.is(false, 0)); // false
console.log(Object.is('', 0)); // false
console.log(Object.is([1, 2], '1,2')); // false
console.log(Object.is(NaN, NaN)); // true
console.log(Object.is(+0, -0)); // false
console.log(Object.is(-0, -0)); // true
console.log(Object.is(+0, +0)); // true
16.isNaN()
与 Number.isNaN()
的区别
1. isNaN()
isNaN()
函数会先尝试将传入的参数转换为数字,然后检查转换后的值是否为 NaN
。这意味着它不仅检测 NaN
本身,还会将那些不能转换为有效数字的值视为 NaN
。
示例:
console.log(isNaN(NaN)); // true
console.log(isNaN('hello')); // true
console.log(isNaN(undefined)); // true
console.log(isNaN({})); // true
console.log(isNaN(123)); // false
console.log(isNaN('123')); // false
2. Number.isNaN()
Number.isNaN()
函数不会进行类型转换,只会在参数本身是 NaN
的情况下返回 true
。它更为严格,只有传入的值是 NaN
时才会返回 true
。
示例:
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN('hello')); // false
console.log(Number.isNaN(undefined)); // false
console.log(Number.isNaN({})); // false
console.log(Number.isNaN(123)); // false
console.log(Number.isNaN('123')); // false
17.==
操作符的强制类型转换规则
==
会在比较两个值时进行强制转换,这种类型转换遵循一套规则,使得不同类型的值可以相互比较。规则如下:
1. null
和 undefined
null
和undefined
仅相等于自身和对方。
示例:
console.log(null == undefined); // true
console.log(null == null); // true
console.log(undefined == undefined); // true
console.log(null == 0); // false
console.log(undefined == 0); // false
2. 布尔类型
- 如果有一个操作数是布尔值,JavaScript 会将布尔值转换为数字,然后再进行比较。
示例:
console.log(true == 1); // true
console.log(false == 0); // true
console.log(true == 2); // false
3. 字符串和数字
- 如果是字符串和数字比较,JavaScript 会将字符串转换为数字,然后再进行比较。
示例:
console.log('42' == 42); // true
console.log('42' == '42'); // true
console.log('42' == 43); // false
console.log('0' == false); // true
4. 对象和原始类型
- 如果有一个操作数是对象,另一个是原始类型(字符串、数字、布尔值),JavaScript 会尝试调用对象的
toPrimitive
方法(valueOf
或toString
)将对象转换为原始类型,然后再进行比较。
示例:
console.log([1, 2] == '1,2'); // true
console.log([1] == 1); // true
console.log({} == '[object Object]'); // true
5. 特殊情况
- 空字符串:空字符串会被转换为数字
0
进行比较。
示例:
console.log('' == 0); // true
console.log('' == false); // true
- 对象转换为原始类型:对象的比较会触发类型转换,通过调用
toPrimitive
方法(valueOf
或toString
),转换为原始类型后再比较。
示例:
let obj = { toString: () => '42' };
console.log(obj == '42'); // true
console.log(obj == 42); // true
Symbol
类型:Symbol
类型只能与Symbol
类型进行比较,与其他类型的比较总是返回false
。
示例:
console.log(Symbol() == Symbol()); // false
console.log(Symbol() == 'symbol'); // false
console.log(Symbol() == false); // false
扩展知识
==
操作符在比较两个不同类型的值时,会根据上述规则进行类型转换。这些规则可以导致一些意想不到的结果,因此在进行值比较时需要格外小心。如果希望避免这些复杂的类型转换规则,推荐使用严格相等操作符 ===
,它不进行类型转换,仅在值和类型都相等时才返回 true
。
18.JavaScript 中如何进行隐式类型转换
隐式类型转换也称为类型强制转换,是指 JavaScript 在表达式求值时自动将一种数据类型转换为另一种数据类型的过程。隐式类型转换主要发生在以下三种情况下:算术运算、比较运算和逻辑运算。常见的隐式类型转换规则如下:
1. 算术运算
在算术运算中,JavaScript 会将操作数转换为数字类型。
- 如果操作数中有一个是字符串,且运算符是
+
,则会进行字符串拼接。 - 如果运算符是其他算术运算符(如
-
、*
、/
、%
),则会将操作数转换为数字。
示例:
console.log(5 + "5"); // "55"(字符串拼接)
console.log("5" + 5); // "55"(字符串拼接)
console.log(5 + 5); // 10(数值相加)
console.log(5 - "2"); // 3
console.log("6" * "2"); // 12
console.log("8" / 2); // 4
console.log("10" % 3); // 1
2. 比较运算
在比较运算中,JavaScript 会将操作数转换为相同的类型再进行比较。
-
双等号(
==
):会进行类型转换。console.log(5 == "5"); // true(字符串 "5" 被转换为数字 5) console.log(false == 0); // true(false 被转换为数字 0) console.log(true == 1); // true(true 被转换为数字 1) console.log(null == undefined); // true
-
三等号(
===
):不会进行类型转换,直接比较类型和值。console.log(5 === "5"); // false console.log(false === 0); // false console.log(true === 1); // false console.log(null === undefined); // false
3. 其他比较运算符(>
, <
, <=
, >=
)
操作数会被转换成数字或字符串。
- 数字与数字之间直接比较。
- 字符串与字符串之间按字典序比较。
- 数字与字符串之间,字符串会被转换为数字。
示例:
console.log(5 > "2"); // true
console.log("6" < "12"); // false(字符串比较)
console.log("8" >= 8); // true
console.log("10" <= 20); // true
4. 逻辑运算符
逻辑运算符(!
, ||
, &&
)会将操作数转换为布尔值。
-
逻辑非(
!
):console.log(!0); // true(0 被转换为 false,然后取反为 true) console.log(!1); // false(1 被转换为 true,然后取反为 false) console.log(!""); // true(空字符串被转换为 false,然后取反为 true) console.log(!"hello"); // false(非空字符串被转换为 true,然后取反为 false)
-
逻辑或(
||
):console.log(0 || 1); // 1(0 被转换为 false,因此返回第二个操作数 1) console.log(1 || 0); // 1(1 被转换为 true,因此返回第一个操作数 1)
-
逻辑与(
&&
):console.log(0 && 1); // 0(0 被转换为 false,因此返回第一个操作数 0) console.log(1 && 2); // 2(1 被转换为 true,因此返回第二个操作数 2)
5. 字符串与数字之间的转换
字符串与数字之间的隐式转换在很多情况下都会发生。
- 如果操作符是
+
,且有一个操作数是字符串,则会进行字符串拼接。 - 如果操作符是其他算术运算符(如
-
、*
、/
、%
),则会将字符串转换为数字。
示例:
console.log("5" - 2); // 3("5" 被转换为数字 5,然后 5 - 2 = 3)
console.log("5" * "2"); // 10(两个字符串都被转换为数字)
console.log("5" / 2); // 2.5("5" 被转换为数字 5,然后 5 / 2 = 2.5)
6. +
操作符的特殊规则
+
操作符的行为取决于操作数的类型:
-
字符串拼接:如果两个操作数中有一个是字符串,则
+
操作符将执行字符串的拼接。console.log("hello" + " world"); // "hello world" console.log(5 + "5"); // "55" console.log("The answer is " + 42); // "The answer is 42"
-
数值加法:如果两个操作数都是数字,或者一方是布尔值或
null
,则+
操作符将执行数值加法(隐式转换为数字再执行运算)。console.log(5 + 5); // 10 console.log(true + 1); // 2 (true 被转换为 1) console.log(null + 1); // 1 (null 被转换为 0)
-
对象转换为原始值:如果操作数是对象,它们会首先转换为原始值,然后再根据原始值的类型进行操作。
console.log([1, 2, 3] + [4, 5, 6]); // "1,2,34,5,6" console.log({} + {}); // "[object Object][object Object]"
-
数字和字符串:优先转为字符串进行处理。
console.log(10 + "20"); // "1020" console.log("10" + 20); // "1020"
-
布尔值和字符串:
console.log(true + "test"); // "truetest" console.log(false + "test"); // "falsetest"
总结:当字符串与其他基础类型相加时,会进行字符串拼接。如果操作数是对象,会先转换为原始值,再根据原始值进行操作。
19.JavaScript 中为什么会有 BigInt 的提出?
1. 背景
JavaScript 中的数字类型是基于双精度浮点数实现的,这种方式在大多数情况下是足够的,但在处理非常大的整数时,会出现精度问题。为了处理和表示任意精度的整数,JavaScript 引入了 BigInt
类型。
BigInt
可以处理任意大的整数且不会丢失精度,从而解决了大整数运算中的问题。
示例:
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(Number.MAX_SAFE_INTEGER + 1); // 9007199254740992
console.log(Number.MAX_SAFE_INTEGER + 2); // 9007199254740992 (错误)
2. BigInt 的优势
- 支持任意大整数:
BigInt
可以表示任意大的整数,而不会丢失精度。 - 专门设计用于整数运算:与浮点数不同,
BigInt
专门用于整数运算,确保了精度和一致性。 - 与
Number
类型区别明确:BigInt
是一种新的原始数据类型,与现有的Number
类型区别明确,避免了混淆。
3. 使用方法
使用 BigInt
非常简单,只需在整数后加上 n
后缀,或者使用 BigInt
构造函数。
示例:
const bigInt1 = 1234567890123456789012345678901234567890n;
const bigInt2 = BigInt("1234567890123456789012345678901234567890");
console.log(bigInt1); // 1234567890123456789012345678901234567890n
console.log(bigInt1 + 1n); // 1234567890123456789012345678901234567891n
4. 注意事项
BigInt
和 Number
类型不能直接混合使用,需要进行显式转换。
示例:
const num = 42;
const bigInt = 12345678901234567890n;
console.log(num + bigInt); // TypeError: Cannot mix BigInt and other types
// 需要显式转换
console.log(BigInt(num) + bigInt); // 12345678901234567932n
总结
- 隐式类型转换:JavaScript 在算术运算、比较运算和逻辑运算中会自动进行类型转换,但这种转换可能会导致一些意想不到的结果。因此,建议尽量使用严格相等操作符(
===
)和显式类型转换。 - BigInt:
BigInt
的引入解决了 JavaScript 在处理大整数时的精度问题,适用于需要高精度整数运算的场景。