js数据类型转换(二) -- 细节拷打

140 阅读8分钟

前言

大家好 , 我是一名在校大二学生 , 今日整理之前文章 , 发现学习了很多 js 数据类型的知识 。

而今日学习又忽逢数据类型转换细节问题 , 让我再度引起重视 , 遂有此文 。

无字动图.gif

有了第一篇文章的基础 , 这一次 , 我将以单刀直入的方式 , 直接给出代码 , 一起来思考所以然 ~

往期相关文章如下 :

js数据类型转换(一) -- 显示转换与隐式转换

知道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):由于合法的进制范围是从 236,进制数为 1是不合法的,所以返回 NaN
  • 对于 parseInt(3, 2):二进制数中只有 01这两个有效数字,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"),分别得到 123,所以最终返回 [1, 2, 3]

3. [1,2,3].map((val,idx)=>parseInt(val,idx))的情况

  • 第一次迭代:val1idx0,相当于 parseInt("1", 0),按前面说的会按十进制解析得到 1
  • 第二次迭代:val2idx1,相当于 parseInt("2", 1),因为进制 1不合法,返回 NaN
  • 第三次迭代:val3idx2,相当于 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进行解析并返回结果,

其实际的解析和前面的逻辑一样,根据每次传入的 vidx按照 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 的大体内容 , 在我的文章中都有讲述 :

js数据类型转换(一) -- 显示转换与隐式转换

这里重点聊一聊 : 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

逐行解释 :

  1. console.log(String()) // ''

    • String() 作为构造函数调用时,如果没有参数,返回一个空字符串 ''。(同Boolean😀)
  2. console.log(String(123)) // '123'

    • String(123) 将数字 123 转换为字符串 '123'
  3. console.log(String(true)) // 'true'

    • String(true) 将布尔值 true 转换为字符串 'true'
  4. console.log(String(false)) // 'false'

    • String(false) 将布尔值 false 转换为字符串 'false'
  5. console.log(String(null)) // 'null'

    • String(null)null 转换为字符串 'null'
  6. console.log(String(undefined) , typeof String(undefined)) // 'undefined'

    • String(undefined)undefined 转换为字符串 'undefined'
    • typeof String(undefined) 返回字符串 'string',表示 String(undefined) 的类型是字符串。
  7. console.log(String(NaN)) // 'NaN'

    • String(NaN)NaN 转换为字符串 'NaN'
  8. console.log(String(Symbol('abc'))) // 'Symbol(abc)'

    • Symbol('abc') 创建一个新的 Symbol 对象,描述为 'abc'
    • String(Symbol('abc')) 将这个 Symbol 对象转换为字符串 'Symbol(abc)'
  9. console.log(String(Symbol.iterator)) // 'Symbol(Symbol.iterator)'

    • Symbol.iterator 是一个内置的 Symbol 值,用于定义对象的默认迭代器。
    • String(Symbol.iterator) 将这个内置的 Symbol 对象转换为字符串 'Symbol(Symbol.iterator)'
  10. console.log(String(Symbol.for('abc'))) // 'Symbol(abc)'

    • Symbol.for('abc') 检索全局 Symbol 注册表中键为 'abc' 的 Symbol,如果不存在则创建一个新的 Symbol 并注册。
    • String(Symbol.for('abc')) 将这个 Symbol 对象转换为字符串 'Symbol(abc)'
  11. console.log(String(Symbol.for('abc')).description) // undefined

    • Symbol.for('abc').description 获取这个 Symbol 对象的描述字符串 'abc'
    • 但是,String(Symbol.for('abc')) 返回的是整个 Symbol 对象的字符串表示 'Symbol(abc)',而不是原始的 Symbol 对象。
    • 因此,String(Symbol.for('abc')).description 实际上是对字符串 'Symbol(abc)' 调用 description 属性,而字符串对象没有 description 属性,所以结果是 undefined
  12. console.log(+0,-0) // 0 -0

    • +0-0 是 JavaScript 中的两个不同的值,尽管它们在某些情况下表现相似。
    • console.log(+0, -0) 输出 0 -0,表示这两个值分别是正零和负零。

String总结

  • String() 作为构造函数调用时,如果没有参数,返回空字符串 ''
  • 数字、布尔值、nullundefined 都可以转换为相应的字符串表示。
  • NaN 转换为字符串 'NaN'
  • Symbol 对象转换为字符串时,格式为 'Symbol(描述)'
  • String(Symbol(...)).description 返回 undefined,因为 String(Symbol(...)) 返回的是字符串,而不是原始的 Symbol 对象。
  • +0-0 是不同的值,但在大多数情况下表现相似

关于Symbol的相关知识 ,可以看我的这篇文章 :

JavaScript中不可忽略的Symbol 在前端开发领域,尤其是处理复杂的大型项目时,JavaScript的数据类型 - 掘金 (juejin.cn)

感悟

JS过于优雅 , 总让我感觉我配不上她 , 还是要加强学习 !

爱情神话.gif