2-值得类型转化(number)

99 阅读4分钟

0.1 + 0.2 !== 0.3

  • 所有的值在计算机中都是按照二进制存储的,运算也是基于二进制来的
    • 证书运算一般不会出现问题,出安全数范围外除外
    • 小数运算一般会出现问题
  • 浮点数转为二进制会出现"无限"循环的情况,计算机底层存储的时候按照可以识别的最长位数进行存储,直接干掉...所以浮点数存储的二进制本身的失去精准度了
    • 所以最后运算也是失精准度的,而小数最后面全是0会去掉
// 扫盲: 把十进制转为二进制
// 整数: 除2取余,最后倒着拼接
// 小数: × 2, 取整一直到结果是1为止
// => [10进制value].toString(2)

0.1十进制 -> 2进制
0.1 * 2 = 0.2  0
0.2 * 2 = 0.4  0
0.4 * 2 = 0.8  0
0.8 * 2 = 1.6  1
0.6 * 2 = 1.2  1
0.2 * 2 = 0.4  0
...
// => 没有任何情况下为1

运算保证精准度,实现思路: 把小数变为整数(乘以系数)运算,结果再除以系数

// => 计算系数
const coefficient = function (num) {
  num = String(num);
  let [,char=''] = num.split('.');
  let len = char.length;
  return Math.pow(10, len);
}

const plus = function (n, m) {
  n = Number(n)
  m = Number(m)
  // => 是否是正常number
  if (isNaN(n) || isNaN(m)) return NaN;
  // => 求最大系数
  let coffic = Math.max(coefficient(n), coefficient(m));
  return (n * coffic + m * coffic) / coffic;
}

plus(0.1, 0.2);// 0.3

js中的数据转换

  • 隐式转换(浏览器私底下转换的)和显示转换

Number([val])

  • 一般用于浏览器的隐式转换中
    • 数学运算
    • isNaN检测
    • == 比较
  • 规则:
    • 字符串转数字: 空字符串转为0, 如果出现任何的非有效数字字符,结果都是NaN
    • 把布尔转为数字: true -> 1 false -> 0
    • null -> 0 undefined -> NaN
    • Symbol无法转为数字,会爆错 Uncaught TypeError: Cannot convert a Symbol value to a number
    • BigInt 去除"n" (超过安全数字的,会按照科学计数法处理)
    • 把对象转为数字:
      • 先看是否有Symbol.toPrimitive 这个发方法, 有调用,如果不存在
      • 再调用 valueOf 获取原始值, 如果获取的值不是原始值
      • 再调用对象 toString 把其变为字符串
      • 再把字符串用Number方法转为数字
// => 一般的情况
Number('')
// => 0

Number(null)
// => 0

Number(undefined)
// => NaN

Number(Symbol())
// => Uncaught TypeError: Cannot convert a Symbol value to a number

Number(BigInt(123))
// => 123

Number(true)
// => 1

Number(false)
// => 0
  • Number对象

单元素数字数组

// => [10] 
Number([10])// => 10
// => 流程如下

[10][Symbol.toPrimitive]
// => undefined

[10].valueOf()
// => [10]

[10].toString()
// => '10'

Number('10')
// => 10

多元素数字数组

// => [20, 30]
Number([10, 20]) // => NaN

// => 流程如下
[10, 20][Symbol.toPrimitive] 
// => undefined

[10, 20].valueOf()
// => [10, 20]

[10, 20].toString()
// => '10,20'

Number('10,20')
// => NaN
  • 普通对象
let obj = {0 : 10, length: 1}

Number(obj) // => NaN

// => 流程如下
obj[Symbol.toPrimitive]
// => undefined

obj.valueOf()
// => {0: 10, length: 1}

obj.toString()
// => '[object Object]'

Number('[object Object]')
// => NaN
  • 转化Date对象
let time = new Date()

Number(time) // => 1646574101787
// => 流程如下 

time[Symbol.toPrimitive]
// => ƒ [Symbol.toPrimitive]() { [native code] } 存在

time[Symbol.toPrimitive]('number')
// => 1646574101787

time[Symbol.toPrimitive]('default')
// => 'Sun Mar 06 2022 21:41:41 GMT+0800 (中国标准时间)'

time[Symbol.toPrimitive]('string')
// => 'Sun Mar 06 2022 21:41:41 GMT+0800 (中国标准时间)'
//=> 这个方法可以传三个参数 'number/string/default' => 除了Number(对象)按照这个流程
// => String(对象) 把对象变成字符串也按照这个规则处理,如果存在Symbol.toPrimitive,则执行传递的是'string'

ParseInt([val], [redix]) parseFloat([val])

  • 一般用于手动转换
  • 规则: [val]必须是一个字符串,如果不是字符串;然后从字符串左侧第一个字符开始查找,把找到的有效数字获取出来,遇到非有效数字停止查找;parseFloat可以多识别一个小数点
    • redix: 把找到的内容当作[redix]进制值转为10进制
    • redix:无值时默认是10进制:但是'0x'开头的默认是16进制
    • redix: 取值范围[2-36],不在范围内的一般处理结果都是NaN(个别看浏览器)
parseInt(123, 3) // => 5

// 3进制没有3,所以走如下
// => 1*3^1 + 2*3^0 => 3 + 2 = 5

parseInt('0x1236') // => 4662
// => 1 * 16^3 + 2 * 16^2 + 3 * 16^1 + 6 * 16^0 => 4096 + 512 + 48 + 6 => 4662

鄙视题

let arr = [27.2, 0, '0013', 123]
arr = arr.map(parseInt)// => [27, NaN, 1, 5]

// => 解析如下
parseInt(27.2, 0)
// => 27

parseInt(0, 1)
// => NaN

parseInt('0013', 2)
// => 1

parseInt(123, 3)
// => 5