JavaScript 是一种动态类型语言,这意味着变量的类型可以在程序运行时改变。
JS 的基本数据类型
在 ES6 之前(ES6之后引入了BigInt和Symbol,咱们先按住不表),JavaScript 定义了以下几种基本数据类型:
-
Primitive(原始类型) :这些类型的值是按值复制的,存储于栈内存中。
String
:字符串Number
:数字,包括整数和浮点数Boolean
:布尔值,即true
或false
Null
:空对象指针Undefined
:未定义
-
Complex(复杂类型) :这些类型的值是引用类型,通常存储在堆内存中。
Object
:对象,可以包含多个属性和方法
类型转换的原因
JavaScript 允许不同类型之间的转换,这主要发生在需要不同类型的运算或比较时。例如,当你尝试将一个字符串与一个数字相加时,JavaScript 会自动将字符串转换为数字或反之亦然,以完成操作。
显式类型转换
显式类型转换指的是程序员主动进行的数据类型转换。我们可以使用构造函数或内置方法来进行转换:
Boolean 类型转换
-
转换为
false
的情况:- 空字符串 (
""
) - 数字
0
、+0
和-0
null
undefined
NaN
- 空字符串 (
-
转换为
true
的情况:除了上述所有情况外的一切值都将被转换为true
console.log(Boolean()); // 默认值为false
console.log(Boolean(false)); // false
console.log(Boolean(true));// true
console.log(Boolean(undefined)); // false
console.log(Boolean(null));// false
console.log(Boolean(+0),'+0');// false +0
console.log(Boolean(-0),'-0');// false -0
console.log(Boolean(NaN),'NaN');// false NaN
console.log(Boolean(''),'空字符串');// false 空字符串
Number 类型转换
- 使用
Number()
函数可以将其他类型转换为数字。 - 对于非数字字符串,如果可以解析为有效数字,则转换成功;否则返回
NaN
。
console.log(Number());// 0
console.log(Number(undefined));// NaN
console.log(Number(null));// 0
console.log(Number(false));// 0
console.log(Number(true));// 1
console.log(Number("123"));// 123
console.log(Number("-123"));// -123
// 十六进制
console.log(Number("0x11"));// 17
console.log(Number(""),Number(" "));// 0 0
// 科学计数法
console.log(Number("123e3"));// 123000
console.log(Number("123e-3"));// 0.123
console.log(Number("100a"));// NaN
String 类型转换
- 使用
String()
函数可以将任何类型转换为字符串表示形式。
console.log(String());// 啥都看不到
console.log(String(undefined),typeof String(undefined)); //undefined string
console.log(String(null),typeof String(null)); //null string
console.log(String(+0),typeof String(-0));// 0 string
console.log(String(NaN));// NaN
console.log(String(1));// 1
这里你可能会想问,undefined和null为什么会变成字符串"undefined"和"null"呢?String()
一样啥也看不到呢?
这个其实是无奈之举,因为如果都是看不到输出结果,那你就没办法区分这三种到底是谁被输出了,因此为了让结果可视化,JavaScript做了这样的设计。
隐式类型转换
隐式类型转换是指由 JavaScript 引擎自动执行的类型转换,这通常发生在运算符两边的操作数类型不匹配的情况下。
// 布尔转换
if ("Hello") {
console.log("非空字符串被视为true"); // 输出: 非空字符串被视为true
}
if (0) {
console.log("不会输出,因为数字0被视为false");
} else {
console.log("数字0被视为false"); // 输出: 数字0被视为false
}
// 数字转换
console.log(100 + " bottles of beer on the wall");
// 输出: "100 bottles of beer on the wall" (+操作符导致数字与字符串连接)
console.log("3" * "5");
// 输出: 15 (两个字符串被转换为数字进行乘法运算)
// 字符串转换
var obj = {
toString: function() {
return "object to string";
}
};
console.log("Object as string: " + obj);
// 输出: "Object as string: object to string"
// 对象到原始值的转换
console.log(new Date() == new Date().toString());
// 输出: true 或 false, 取决于环境,但通常会是false,因为两边都是不同的对象实例
// == 操作符
console.log("5" == 5);
// 输出: true (字符串 "5" 被转换为数字 5 进行比较)
// + 操作符
console.log(1 + "2");
// 输出: "12" (数字 1 被转换为字符串并与 "2" 连接)
console.log(1 + 2);
// 输出: 3 (两个数字相加)
特殊数值
console.log(1 / +0); // 正无穷大 Infinity
console.log(1 / -0); // 负无穷大 -Infinity
console.log(Object.is(5,5));// true
console.log(Object.is(+0,-0));// false
// 隐式类型转换
// NaN Not a Number
console.log(2 * "a",2 + "a");// NaN NaN2a
console.log(typeof NaN) // number
console.log(parseInt("abc"));// NaN
console.log(parseInt("123abc"));// 123
console.log(NaN === NaN);// NaN不代表确切值
// 不能通过===NaN 去判断, 所以要isNaN
console.log(isNaN(NaN),isNaN(parseInt("abc")));// true false
NaN
NaN
代表“非数字”(Not-a-Number),它用于表示一个本应是数字但计算失败的结果。
NaN
的特性
-
自我不等:
NaN
有一个独特的行为,即它与任何值都不相等,包括它自己。这意味着如果你尝试使用==
或===
来比较NaN
和任何其他值(甚至NaN
自身),结果总是false
。console.log(NaN === NaN); // false
-
不可用作索引:由于
NaN
不是一个有效的数组索引,如果用NaN
作为数组的索引,它会被转换为0
。 -
传播性:在数学运算中,如果任意操作数是
NaN
,那么结果也将是NaN
。
如何检查 NaN
因为 NaN !== NaN
,所以不能通过简单的比较来检测 NaN
。JavaScript 提供了两个内置函数来帮助你确定一个值是否是 NaN
:
-
isNaN() :这个全局函数试图将给定的参数转换为数字,并且只有在转换后的结果是
NaN
时才返回true
。但是,它的行为有时不符合预期,因为它会尝试强制类型转换,这可能导致一些意外的结果。 -
Number.isNaN() :这是 ES6 引入的一个更可靠的检测
NaN
的方法。它不会尝试强制类型转换,因此只会在参数确实是NaN
时返回true
。console.log(Number.isNaN(NaN)); // true console.log(Number.isNaN(42)); // false
NaN
的用途
- 错误处理:当你执行可能失败的数值计算或转换时,
NaN
可以作为一种信号,表明发生了错误或输入无效。 - 数据验证:可以用来检查用户输入或其他来源的数据是否能被正确地解析为数字。
- 算法和计算:在某些算法中,
NaN
可以用来表示未定义或缺失的数值。
面试会考的parseInt
console.log([1,2,3].map(parseInt));
这段代码的结果是什么?从直觉来看我想你会认为就是[1,2,3]
。
但是这段代码不会如你预期的那样工作,原因在于 parseInt
函数与 Array.prototype.map
方法结合使用时,传递给 parseInt
的参数和期望的行为不匹配。
解析问题
Array.prototype.map
方法会为数组中的每个元素调用提供的函数,并将三个参数传递给它:
- 当前元素 (
currentValue
):数组中正在处理的元素。 - 索引 (
index
):当前元素在数组中的索引。 - 数组本身 (
array
):被遍历的数组。
而 parseInt
函数接受两个参数:
- 字符串 (
string
):要解析的字符串。 - 进制 (
radix
):表示字符串中数字的进制(可选)。
当 map
调用 parseInt
时,它实际上是在以以下方式调用 parseInt
:
parseInt(currentValue, index);
这意味着:
- 第一个参数是你要解析的值(这通常是正确的)。
- 第二个参数是数组中该元素的索引,而不是你想要指定的进制。对于非零索引,这会导致意外的结果,因为
parseInt
将尝试使用这些索引来解析整数。
示例
让我们看看具体会发生什么:
console.log([1, 2, 3].map(parseInt));
// 输出: [1, NaN, NaN]
解释输出结果:
- 对于第一个元素
1
,parseInt(1, 0)
返回1
(因为基数0
默认解释为十进制)。 - 对于第二个元素
2
,parseInt(2, 1)
返回NaN
,因为在一进制中没有有效的数字表示。 - 对于第三个元素
3
,parseInt(3, 2)
返回NaN
,因为3
不是一个有效的二进制数。
正确的做法
parseInt(string, radix) 解析一个字符串并返回指定基数的十进制整数,radix
是 2-36 之间的整数,表示被解析字符串的基数。
如果你的目标是将数组中的所有元素转换为整数(假设它们已经是整数),你可以直接使用箭头函数或普通的函数表达式来确保只传递一个参数给 parseInt
:
console.log([1, 2, 3].map(function(x) { return parseInt(x, 10); }));
// 或者使用箭头函数:
console.log([1, 2, 3].map(x => parseInt(x, 10)));
// 输出: [1, 2, 3]
或者,由于数组中的元素已经是数字,不需要进行 parseInt
转换:
console.log([1, 2, 3].map(Number)); // 使用 Number 构造函数也可以
// 输出: [1, 2, 3]
当然还有一种曲线救国的办法,那就是传给parsInt第三个参数:
console.log([1,2,3].map((v,index,item) =>{
console.log(v,index,item);
return parseInt(v,index,item)
}))
// 输出:
[ 1, NaN, NaN ]
1 0 [ 1, 2, 3 ]
2 1 [ 1, 2, 3 ]
3 2 [ 1, 2, 3 ]
[ 1, NaN, NaN ]
希望这篇文章对你有帮助,点个赞再走吧~