前言
大家好 , 我是一名在校大二学生 , 今日整理之前文章 , 发现学习了很多 js 数据类型的知识 。
而今日学习又忽逢数据类型转换细节问题 , 让我再度引起重视 , 遂有此文 。
有了第一篇文章的基础 , 这一次 , 我将以单刀直入的方式 , 直接给出代码 , 一起来思考所以然 ~
往期相关文章如下 :
倔友有兴致 , 可以随手给赞 🤡
下面直接先给出全场压轴 parseInt
parseInt
首先直接看这道最考验基本功的问题 :
倔友是否可分析出控制台输出结果🤔
console.log([1,2,3].map(parseInt)) // [1, NaN, NaN]
// why ?
//parseInt(1,0)
//parseInt(2,1)
//parseInt(3,2)
console.log("------------------------------------------")
console.log([1,2,3].map((val) => parseInt(val))) // [1, 2, 3]
console.log("------------------------------------------")
console.log([1,2,3].map((val,idx)=>parseInt(val,idx))) //
console.log("------------------------------------------")
console.log([1,2,3].map((v,idx,item) =>{
console.log(v,idx,item)
return parseInt(v,idx)
}))
结果如下
是否和你想的是一致的🤡
这道题目有两个关键点需要理解
- 数组 map 的运行机制
- parseInt 的参数规则
这里给出 关于 parseInt MDN 文档 :parseInt - JavaScript | MDN (mozilla.org)
详细解释如下
1. [1,2,3].map(parseInt)输出 [1, NaN, NaN]的原因
在JavaScript中,map函数会遍历数组中的每个元素,并将当前元素作为回调函数(这里是 parseInt)的第一个参数传入,同时还会传入当前元素的索引作为第二个参数(可选的第三个参数是原数组本身,但通常很少用到这个情况)。
parseInt函数可以接收两个参数,第一个参数是要解析的字符串(在 map中会自动将数组元素转为字符串传递过来),第二个参数是指定的进制数。
- 对于
parseInt(1, 0):当第二个参数为0时,parseInt会根据要解析的字符串开头来自动判断进制,对于字符串"1",它会按十进制解析,所以得到结果1。 - 对于
parseInt(2, 1):由于合法的进制范围是从2到36,进制数为1是不合法的,所以返回NaN。 - 对于
parseInt(3, 2):二进制数中只有0和1这两个有效数字,3不是有效的二进制表示,所以也返回NaN。
所以 [1,2,3].map(parseInt)最终返回 [1, NaN, NaN]。
2. [1,2,3].map((val) => parseInt(val))输出 [1, 2, 3]的原因
这里使用了箭头函数来明确地只将数组元素(通过参数 val接收)传递给 parseInt函数,没有传入第二个参数(索引值),这样 parseInt函数会按照默认的十进制来解析每个元素对应的字符串表示,就相当于执行了 parseInt("1")、parseInt("2")、parseInt("3"),分别得到 1、2、3,所以最终返回 [1, 2, 3]。
3. [1,2,3].map((val,idx)=>parseInt(val,idx))的情况
- 第一次迭代:
val是1,idx是0,相当于parseInt("1", 0),按前面说的会按十进制解析得到1。 - 第二次迭代:
val是2,idx是1,相当于parseInt("2", 1),因为进制1不合法,返回NaN。 - 第三次迭代:
val是3,idx是2,相当于parseInt("3", 2),由于3不是有效的二进制表示,返回NaN。
所以最终返回的数组类似 [1, NaN, NaN](具体结果和前面类似情况一致)。
4. [1,2,3].map((v,idx,item) =>{ console.log(v,idx,item) return parseInt(v,idx) })的情况
这个 map回调函数内部先打印出了当前元素的值 v、索引 idx以及整个原数组 item,然后按照当前元素值和索引来调用 parseInt进行解析并返回结果,
其实际的解析和前面的逻辑一样,根据每次传入的 v和 idx按照 parseInt的规则来处理,最终返回的数组结果也是类似前面提到的根据每次 parseInt调用是否合法、能否正确解析等情况来决定具体元素是正常数值还是 NaN。
关键在于理解 map函数传递参数给回调函数的机制以及 parseInt函数对于进制参数的要求和处理逻辑,这样就能明白不同写法下产生不同结果的原因了。
Boolean
// Primitive -> Boolean
console.log(Boolean())
console.log(Boolean(true))
console.log(Boolean(false))
思考 : console.log(Boolean())输出为什么是 false ?
底层细节如下
Boolean()作为构造函数调用时,如果没有传递任何参数,它会返回一个默认值false。- 这是因为
Boolean构造函数在没有参数的情况下,会创建一个布尔对象,并将其内部值设置为false。- 当
Boolean作为普通函数调用时(即不使用new关键字),它会返回一个布尔值,而不是一个布尔对象。在这种情况下,没有参数同样会被解析为false。
所以我们可以推测 : console.log(new Boolean())的输出
如上第二点所说 , 通过构造函数调用(使用 new 关键字) 没有参数的情况下,会创建一个布尔对象,并将其内部值设置为 false。
NaN
这里分享一些值为 NaN 的情况
// 值为NaN的情况
console.log(Math.sqrt(-1))
console.log(0/0)
console.log(Number('asd'))
// 判断是否是NaN
console.log(NaN === NaN) //不代表确切值 false
// isNaN
console.log(isNaN(NaN),isNaN(parseInt('asd')))//true , true
关于 console.log(NaN === NaN) //不代表确切值 false是因为
NaN是一个特殊的数值,表示“不是一个数字”(Not-a-Number)。- 根据 ECMAScript 规范,
NaN与任何值(包括它自己)进行严格相等比较 (===) 都会返回false。- 这是因为
NaN被设计成一个不可比较的值,以确保在计算过程中出现错误时不会误判。- 如果你需要检查一个值是否是
NaN,应该使用Number.isNaN()或isNaN()函数。
Number
console.log(Number()) //默认为0
//注意区分 : undefined 和 null 区别
console.log(Number(undefined)) // NaN
console.log(Number(null)) // 0
//
console.log(Number(true)) // 1
console.log(Number(false)) // 0
console.log(Number('123')) // 123
console.log(Number('-123')) // -123
console.log(Number("+123")) // 123
//16进制
console.log(Number('0xff')) // 255
console.log(Number(""),Number(" ")) // NaN
console.log(Number("100a")) // NaN
这里的 Number 的大体内容 , 在我的文章中都有讲述 :
这里重点聊一聊 : Number(undefined)和 Number(null)
我之前在知道js数据类型的你 , 是否能够接受这些拷打 !😫
这篇文章中 , 讲到过 null 和 undefined 的区别 , 片段如下 :
思考如下 :
undefined:
表示一个未定义的值,通常用于变量未被赋值或对象属性不存在的情况。
当
undefined转换为数字时,结果是NaN(Not-a-Number),因为undefined本身不是一个有效的数字。
null:
- 表示一个空值或不存在的对象。
- 当
null转换为数字时,结果是0,因为null被视为一个空值,可以合理地转换为0。
总结的说 ,就是 null 表示有值 , 即使是空值或不存在的对象 ,也是有值 ,
而 undefined 是无值 , 由 js 自动检测出 undefined , 不是我们手动赋值
+0 , -0 , 0
console.log(1/+0);
console.log(1/(-0));
console.log(1/0);
// object.is是断言式函数,用来判断两个值是否相等
console.log(Object.is(+0,-0))
这里极具迷惑性 , 在其他语言里都要抛异常了 , js 就是这么桀骜不驯 , 我喜欢🤡
String
console.log(String()) // ''
console.log(String(123)) // '123'
console.log(String(true))// 'true'
console.log(String(false)) // 'false'
console.log(String(null)) // 'null'
console.log(String(undefined) , typeof String(undefined)) // 'undefined'
console.log(String(NaN)) // 'NaN'
console.log(String(Symbol('abc'))) // 'Symbol(abc)'
console.log(String(Symbol.iterator)) // 'Symbol(Symbol.iterator)'
console.log(String(Symbol.for('abc'))) // 'Symbol(abc)'
console.log(String(Symbol.for('abc')).description) // undefined
console.log(+0,-0) // 0 -0
逐行解释 :
-
console.log(String()) // ''String()作为构造函数调用时,如果没有参数,返回一个空字符串''。(同Boolean😀)
-
console.log(String(123)) // '123'String(123)将数字123转换为字符串'123'。
-
console.log(String(true)) // 'true'String(true)将布尔值true转换为字符串'true'。
-
console.log(String(false)) // 'false'String(false)将布尔值false转换为字符串'false'。
-
console.log(String(null)) // 'null'String(null)将null转换为字符串'null'。
-
console.log(String(undefined) , typeof String(undefined)) // 'undefined'String(undefined)将undefined转换为字符串'undefined'。typeof String(undefined)返回字符串'string',表示String(undefined)的类型是字符串。
-
console.log(String(NaN)) // 'NaN'String(NaN)将NaN转换为字符串'NaN'。
-
console.log(String(Symbol('abc'))) // 'Symbol(abc)'Symbol('abc')创建一个新的 Symbol 对象,描述为'abc'。String(Symbol('abc'))将这个 Symbol 对象转换为字符串'Symbol(abc)'。
-
console.log(String(Symbol.iterator)) // 'Symbol(Symbol.iterator)'Symbol.iterator是一个内置的 Symbol 值,用于定义对象的默认迭代器。String(Symbol.iterator)将这个内置的 Symbol 对象转换为字符串'Symbol(Symbol.iterator)'。
-
console.log(String(Symbol.for('abc'))) // 'Symbol(abc)'Symbol.for('abc')检索全局 Symbol 注册表中键为'abc'的 Symbol,如果不存在则创建一个新的 Symbol 并注册。String(Symbol.for('abc'))将这个 Symbol 对象转换为字符串'Symbol(abc)'。
-
console.log(String(Symbol.for('abc')).description) // undefinedSymbol.for('abc').description获取这个 Symbol 对象的描述字符串'abc'。- 但是,
String(Symbol.for('abc'))返回的是整个 Symbol 对象的字符串表示'Symbol(abc)',而不是原始的 Symbol 对象。 - 因此,
String(Symbol.for('abc')).description实际上是对字符串'Symbol(abc)'调用description属性,而字符串对象没有description属性,所以结果是undefined。
-
console.log(+0,-0) // 0 -0+0和-0是 JavaScript 中的两个不同的值,尽管它们在某些情况下表现相似。console.log(+0, -0)输出0 -0,表示这两个值分别是正零和负零。
String总结
String()作为构造函数调用时,如果没有参数,返回空字符串''。- 数字、布尔值、
null和undefined都可以转换为相应的字符串表示。 NaN转换为字符串'NaN'。- Symbol 对象转换为字符串时,格式为
'Symbol(描述)'。 String(Symbol(...)).description返回undefined,因为String(Symbol(...))返回的是字符串,而不是原始的 Symbol 对象。+0和-0是不同的值,但在大多数情况下表现相似
关于Symbol的相关知识 ,可以看我的这篇文章 :
JavaScript中不可忽略的Symbol 在前端开发领域,尤其是处理复杂的大型项目时,JavaScript的数据类型 - 掘金 (juejin.cn)
感悟
JS过于优雅 , 总让我感觉我配不上她 , 还是要加强学习 !