JavaScript 高阶版(二)「数据类型转换」

147 阅读6分钟

转换为数字类型

常用的应用场景

  • 数学运算
  • == 比较
  • 手动转换

数学运算

除了"+"运算其他的都是数学运算,加号不仅仅是数学运算,还有字符串拼接

  • "+" 两边都有值,有一边出现字符串或者对象,则为字符串拼接(有特殊性)
    • 原理:调用 Object.prototype.toString 方法
    • 适用范围:只有纯粹的对象才会转换为 [object Object]
      • 原本是想把{}变为数字,但是Symbol.toPrimitive/valueOf/toString,调到toString变为字符串,此时符合了有一边变为字符串了,则是字符串拼接
    • 特殊情况
      • {}+10--> 10:{} 看做是一个代码块(ES6 块级上下文),真正做运算的只有 +10 ,所以最终结果是 10
      • ( {}+10 ) --> '[object Object]10':用小括号包裹起来,里边的内容看做是一个整体
      • 只有一边/ ++x/ x++ ,都是数学运算
        • +'10' --> 10:number 类型
        • 10+(++X):先把 X 累加 1,然后和 10 运算
        • 10+(X++):先和 10 进行运算,最后再把 X 累加 1

思考 x++ == x+=1 == x=x+1? 分析

  • let x = '10';console.log( ++x );
    • 有一边则为数学运算,先变为数字类型 10,然后在 加 1,运算结果赋值给 x 为 11
  • console.log( x+=1 );
    • x+=1 --> x=x+1 --> '10'+1-->'101'

对象类型转换为数字类型

内部机制

  • 首先检测对象的 Symbol.toPrimitive 这个属性,获取原始值
    • Symbol.toPrimitive(hint):hint 检测到浏览器隐式规定的转换类型:'number'/'string'/'default'
  • 如果没有这个属性,则继续调用它的 valueOf(),也是获取原始值(number/string/boolean/null/undefined/symbol/bigint)
  • 如果值不是原始值,则继续调用 toString() 转换为字符串
  • 再把字符串基于 Number(str) 转换为数字

其他类型转换为 number

Number 类型转换规则

Number( undefined ):NaN
Number( null ):+0
Number( true ):1
Number( false ):+0	
Number( 数字 ):返回参数「不需要转换」
Number( symbol 类型 ):抛出类型错误的异常「symbol 不能直接转换为数字」
Number( bigint 类型):返回不带 n 的数字
Number( "" ):空字符串或者空格,返回 0
Number(-10):返回 -10
Number(+10):返回 10

除上述情况外,剩下的情况,只要字符串中出现非有效数字字符,结果为 NaN

隐式转换(手动转换)

  • Number():浏览器内部默认要先转换为 Number 在进行计算的
  • 数学运算
  • 基于==比较的时候
    • 有些值是需要转换为数字在进行比较
  • isNaN([value]) 如果不是数字类型先转换成数字类型 ...

思考?

如何实现让其 obj - 10 = 0?

let obj = {
  name: 'Guyal_',
  [Symbol.toPrimitive]() {
      return 10;
  }
};
console.log(obj-10);  // 0
分析分析

既然,对象存在 Symbol.toPrimitive 这个属性,又可以调用 valueOf/toString 方法,那么,我们完全可以重构这三种方法,以此来实现想要的结果。我们来看实现方法

底层调用 obj 的 Symbol.toPrimitive 属性,获取原始值,进而实现数学运算,最终结果为 0

parseInt/parseFloat?

parseInt

  • 语法:parsetInt( [val],[radix] )
  • 参数:
    • [val]: 必须是一个字符串,如果不是,则也要默认转换为字符串
    • [radix]:不设置(或者写的是零):正常都是按照 10 处理的,如果字符串是以 ”0x“ 开始的,默认值是 16...
  • 返回值:整数
  • 查找机制
    • 从字符串的左侧第一个字符开始,查找有效数字字符(遇到非有效数字字符停止查找,不论后面是否还有数字字符,都不再找了),把找到的有效数字字符转换为数字,如果一个都没找到,结果就是 NaN
  • 处理机制
    • 先在[val]中,找到所有符合 [radix] 进制的内容
    • 从左到右查找,直到遇到不符合的为止「不论后面是否还有符合进制的,都不在查找了」
    • 进制转换
      • 二进制「0~1」
      • 三进制 「0~2」
      • 四进制 「0~3」
      • 八进制 「0~7」
      • 十进制 「0~9」
      • 十六进制 「09 A10F15」
    • 然后再把找到的内容看做[radix]进制,转换为十进制
    • [radix] 范围 2~36,除了 0 以外 (0->10/16),不在这个范围内,结果为NaN

parseFloat([val])

  • 不支持第二个参数
  • 比 parseInt 多识别一个小数点,其他与 parseInt 相同

对象类型转换为字符串类型

应用

  • 字符串拼接

== 和 === 的区别?

  • == 比较规则
    • 两边类型不一致,则转换为相同的数据类型
      • 对象 == 字符串 「对象会转换为字符串」
      • 剩余的情况都是转换为数字
        • 对象 == 数字
        • 字符串 == 布尔
        • 字符串 == 数字
        • 布尔 == 数字
    • 两边类型一致,则开始进行比较;对象比较的是堆内存地址
  • === 比较规则
    • 只要类型不一致,则不会转换,直接返回 false

插一波面试题

输出结果是?

console.log([]==false);
console.log(![]==false);
  • console.log([]==false);
    • 对象==布尔 都转换为数字(隐示转换)
    • 对象转换为数字:
      • [].valueOf 不是一个原始值,是引用类型的值,所以调用 [].toString() 方法,其结果为""(空字符串),
      • 转换为数字
        • "" 转换为 Number 类型为 0,false 转换为 Number类型是 0 ,所以[]==false ==> true
  • console.log(![]==false);
    • !的优先级高于==
    • ![] 首先转换成布尔值,[]转换成布尔值为 true,取反则是 false,所以 false==false ==>true
    • 除了 0/NaN/null/undefined/空字符串,转布尔值为 false,其余都为 true
    • Boolean([]); true
    • !true = false

实现如下代码,使其输出'OK'

var a = ?
if(a == 1 && a == 2 && a == 3) {
   console.lo('OK');
} 
  • 第一类:隐式数据类型转换的时候进行处理
      1. 重构 Symbol.toPrimitive / toString / valueOf 均可
    var a = {
        i: 0
    }
    // 对象==数字,需要让对象转换为数字
    a[Symbol.toPrimitive] = function() {
        return ++this.i
    }
    if(a == 1 && a == 2 && a == 3) {
        console.log('OK');
    } 
    
      1. 数组 toString 方法重构(实现的小技巧)
    var a = [1,2,3];
    // toString 重写为 shift 方法,调用 toString 相当于调用 shift 方法,删除首项,并返回删除项,从而实现
    a.toString = a.shift;
    if(a == 1 && a == 2 && a == 3) {
        console.log('OK');
    } 
    
  • 第二类:ES6 数据劫持
var i = 0;
Object.defineProperty(window, 'a', {
    get() {
        return ++i;
    }
});
if (a == 1 && a == 2 && a == 3) {
    console.log('OK');
}

阿里面试题

let arr = [27.2, 0, '0013', '14px', 123];
arr = arr.map(parseInt);
console.log(arr);

思路

  • 回调函数:把一个函数作为值传递给另外一个函数执行(实参)
  • Map 使用方法
    • 数据中有多少项,就迭代多少次,每一次执行回调函数(item当前迭代项 index索引),支持回调函数返回值,返回啥就把当前项替换成啥,原始数组不变,以新数组返回!! 分析:结果为:[27, NaN, 1, 1, 27]
parseInt(27.2,0) :27
parseInt(0,1):NaN
parseInt('0013',2):'001' 看做2进制 转换为10进制,1*2^0 -> 1
parseInt('14px',3):'1' 看做3进制 转换为10进制,1*3^0 -> 1
parseInt(123,4):'123' 看做4进制 转换为10进制
   3*4^0 + 2*4^1 + 1*4^2 -> 3+8+16 -> 27