JS高阶(二)探索JS中的数据类型转换

153 阅读9分钟

1、把其他数据类转换为Number

Number([val])

  • 一般用于浏览器的隐式转换中

    • 数学运算

    • isNaN检测

    • ==比较

      ...

  • 规则:

    • 字符串转为数字:空字符串变为0,如果出现任何非有效数字字符,结果都是NaN

    • 把布尔转换为数字:true -> 1 false -> 0

    • [] -> 0

    • null -> 0 undefined -> NaN

    • Symbol无法转换为数字,会报错:Uncaught TypeError: Cannot convert a Symbol value to a number

    • BigInt去除“n”(超过安全数字,会按照科学计数法处理)

    • 把对象转为数字:

      • 1.先调用对象的 Symbol.toPrimitive 这个方法,如果不存在这个方法(Symbol.toPrimitive获取对象的原始值,相比valueOf算是新的获取原始值的方法)

        /* xxx[Symbol.toPrimitive](hint){
        // hint:'number'/'string'/'default'
        // + number:获取当前对象的数学类型的原始值
        // + string:获取当前对象的字符串类型的原始值
        // + default:根据操作获取数字或者字符串类型的原始值
        } */
        
        let obj = {
            name: 'zhufeng',
            age: 20,
            [Symbol.toPrimitive](hint) {
                let result;
                switch (hint) {
                    case 'number':
                        result = 0;
                        break;
                    case 'string':
                        result = JSON.stringify(obj);;
                        break;
                    default:
                        result = 1;
                }
                return result;
            }
        }
        console.log(Number(obj)); // hint:"number"
        console.log(String(obj)); // hint:"string"
        console.log(10 + obj); // hint:"default"
        console.log(10 - obj); // hint:"number" */
        
      • 2.再调用对象的 valueOf 获取原始值,如果获取的值不是原始值

      • 3.再调用 toString 把其变为字符串

      • 4.最后再把字符串基于Number方法转为数字

      let obj = {}
      //undefined
      Number(obj)
      //NaN
      obj[Symbol.toPrimitive]
      //undefined
      obj.valueOf()
      //{}
      obj.toString()
      //"[object Object]"
      Number("[object Object]")
      //NaN
      

      let num = new Number(10)
      //undefined
      Number(num)
      //10
      num[Symbol.toPrimitive]
      //undefined
      num.valueOf()
      //10
      Number(num)
      //10
      String(num)
      //"10"
      

      let time = new Date();
      /undefined
      Number(time)
      //1627283629808
      time[Symbol.toPrimitive]
      //ƒ [Symbol.toPrimitive]() { [native code] }
      time[Symbol.toPrimitive]('number')
      //1627283629808
      String(time)
      //"Mon Jul 26 2021 15:13:49 GMT+0800 (中国标准时间)"
      time[Symbol.toPrimitive]('string')
      //"Mon Jul 26 2021 15:13:49 GMT+0800 (中国标准时间)"
      

parseInt([val],[radix]) parseFloat([val])

  • 一般用于手动转换

  • 规则:

    • [val] 值必须是一个字符串,如果不是则先转为字符串;然后从字符串左侧第一个字符开始找,把所有的有效数字字符最后都转为数字[一个都没找到就是NaN];遇到一个非有效数字字符,不论后面是否还有有效数字字符,都不再查找了;parseFloat可以多识别一个小数点;

    • parseInt([val {string}],[radix])

      在parseInt本身处理规则的基础上,还可以设置[radix]进制:在[val]字符串中,从左到右查找符合[radix]进制的内容(遇到一个不符合的则结束查找),把找到的内容当做[radix]进制值最后转换为10进制

      @1 把其它进制转换为10进制 按权展开求和

      '10011'(2进制) -> 10进制

      12^4 + 02^3 + 02^2 + 12^1 + 1*2^0 => 19

      @2 [radix]不设置(或者设置为零),则默认值是10;但是如果字符串是以‘0x’开头的,则默认值是16;

      @3 [radix]有取值范围:2~36,不在范围内,最后处理的结果都是NaN

      ---

      在浏览器中有一个特殊情况:如果数字(不是字符串)是以0开始的,浏览器会认为这应该是8进制的值,它会把8进制默认转化为10进制再处理

      012 -> 10

      08^2+18^1+2*8^0 => 10

      parseInt('10px',10);

      在字符串中找到所有符合十进制的内容 -> '10'

      把找到的 '10' 看作十进制转换为十进制 => 10


      parseInt('1030px',2);

      在字符串中找到所有符合二进制的内容 -> '10'

      把找到的 '10 ' 看作二进制转换为十进制(如何把其它机制的值转换为十进制 “按权展开求和”)

      个位数权重0 十位数权重1 百位数权重2 ...

      1x2^1 + 0x2^0 => 2

  • 练习题:

    let arr = [27.2, 0, '0013', '14px', 123];

    arr = arr.map(parseInt);

    解析:数组中有多少项,回调函数就被执行多少次

    arr.map(function(item, index) {

    //item:当前迭代这一项

    //index:对应的索引

    //函数执行的返回的结果是啥,把数组中对应项替成,原始数组不变,以新数组形式返回

    return 'xxx'

    })

    arr[0] = parseInt(27.2,0);

    parseInt('27.2',10);

    找到符合十进制的内容 -> '27'

    把'27'看作十进制转为十进制 => 27

    arr[1] = parseInt(0,1);

    parseInt('0',1); => NaN 因为一进值不在进制范围之内,会返回NaN

    arr[2] = parseInt('0013',2);

    parseInt('0013',2);

    找到符合二进制的内容 -> '001'

    把'001'看做二进制转为十进制

    0x2^2 +0x2^1 + 1x2^0 => 1

    arr[3] = parseInt('14px',3);

    parseInt('14px',3);

    找到符合三进制的内容 -> '1'

    把'1'看做三进制转为十进制

    1x3^0 => 1

    arr[4] = parseInt(123,4);

    parseInt('123',4);

    找到符合四进制的内容 -> '123'

    把'123'看做四进制转为十进制

    1x4^2 + 2x4^1 + 3x4^0 => 27


2、把其他数据类型转为String

转化规则:

  • 拿字符串包起来
  • 特殊:Object.prototype.toString

出现情况:

  • String([val]) 或者 [val].toString

  • 字符串拼接「“+” 在JS中除了数学运算也有字符串拼接的意思」

    • 有两边,一边是字符串,一定是字符串拼接 代码:[value]+'' => 把[value]转换为字符串
    • 有两边,一边是对象:可能会是字符串拼接,因为其要把对象转换为数字,转换过程中如果遇到转换为字符串,则直接变为字符串拼接;如果遇不到还是数学运算;
    • 只有一边 +[value]:一定是把[value]转换为数字的
  • parseInt([value]) 如果[value]不是字符串也要先变为字符串再处理

  • alert/confirm/prompt/document.write 都会把内容变为字符串,然后再输出

  • ...

把对象obj转为字符串

  • String(obj):Symbol.toPrimitive -> valueOf -> toString 浏览器默认隐式转换是使用String(obj)
  • obj.toString():直接调用这个方法转字符串,不会在执行以上的规则

‘+’有两边,其中一边如果是对象obj,则会

  • 1、调用obj[Symbol.toPrimitive]('default')
  • 2、没有这个属性,则在此调用valueOf
  • 3、valueOf 获取的不是原始值,则继续toString,此时获取的结果是字符串,”+“就变为字符串拼接

console.log(10 + [10]);

  • 没有Symbol.toPrimitive -> valueOf获取的也不是原始值 -> 调用toString ”10“ => "1010"

consoleo.log(10 + {})

  • 没有Symbol.toPrimitive -> valueOf获取的也不是原始值 -> 调用toString "[object Object]" => "10[object Object]"

console.log(10 + new Date());

  • 调用日期的Symbol.toPrimitive('default') 默认转为字符串 => "10Sun Jul 25 2021 11:28:37 GMT+0800 (中国标准时间)"

console.log(10 + new Number(10));

  • new Number(10) -> Number {10} 是一个对象

  • 没有Symbol.toPrimitive -> valueOf 10 => 20

console.log(10 + new String('10'));

  • 没有Symbol.toPrimitive -> valueOf "10" => "1010"

“+”有两边,其中一边是字符串,妥妥的字符串拼接

“+”有两边,剩下的情况一般都是数学运算了


特殊:{} + 10 => 10

10 + {} => "10[object Object]"

let a = {} + 10

a =>"[object Object]10"

({} + 10)=> "[object Object]10"

练习题:

let result = 100 + true + 21.2 + null + undefined + "Tencent" + [] + null + 9 + false;

console.log(result);

122.2 + undefined = NaN;//undefined -> NaN 数字加NaN都等于NaN

NaN + "Tencent" = "NaNTencent"

在往后都是字符串拼接

console.log(result); // "NaNTencentnull9false"

注意:右边为对象或字符串一般都是拼接


3、把其他数字类型转为Boolean

转换规则:

  • 只有 “0/NaN/空字符串/null/undefined” 五个值是false,其余都是true「哪怕是把对象转换为布尔,也不再调用Symbol.toPrimitive那套机制了,直接看是否是五个中的一个即可」
  • 1 -> true 0 -> false

出现情况

  • Boolean([val]) 或者 !/!!

  • 条件判断

    ...


4、‘==’比较时候的相互转换规则

“==”相等,两边数据类型不同,需要先转为相同类型,然后再进行比较

  • 对象==字符串 对象转字符串「Symbol.toPrimitive -> valueOf -> toString」

  • null==undefined -> true null/undefined和其他任何值都不相等

    null===undefined -> false

  • 对象==对象 比较的是堆内存地址,地址相同则相等

  • NaN!==NaN

  • 除了以上情况,只要两边类型不一致,剩下的都是转换为数字,然后再进行比较的

    “===”绝对相等,如果两边类型不同,则直接是false,不会转换数据类型「推荐」

练习题:

console.log([] == false);

都转换为数字 Number([]) => 0 Number(false) => 0

TRUE

console.log(![] == false);

需要先算 ![] => false; []不在转Boolean的五个值内,所以为true

false == false => TRUE


综合练习题

var a = ?;
if (a == 1 && a == 2 && a == 3) {
    console.log('OK');
}

解决方案一:

利用 == 比较的时候,会把对象转换为数字 Number(a)

在转换的过程中会执行一下步骤:

  • Symbol.toPrimitive

  • valueOf

  • toString

  • 把字符串变为数字

可重写其中的一个方法,让它的返回值变成我们自定义的值

let a = {
  i: 0
};
a[Symbol.toPrimitive] = function toPrimitive() {
  // this -> a
  return ++this.i;
};
if (a == 1 && a == 2 && a == 3) {
  console.log('OK');
}

解决方案二:

var a = [1, 2, 3];
a.toString = a.shift;
if (a == 1 && a == 2 && a == 3) {
    console.log('OK');
} 

解决方案三:

在全局上下文中,基于 var/function 声明的变量,并不是给VO(G)设置的全局变量「基于let/const声明的变量才是」,而是给GO(window)全局对象设置的属性 var a=? ==> window.a=?,我们基于数据劫持完成对应的操作

var i = 0;
Object.defineProperty( window , 'a' , {
	get(){
		return ++i;
	}
}
if (a == 1 && a == 2 && a == 3) {
console.log('OK');
}

//Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

备注:应当直接在 Object 构造器对象上调用此方法,而不是在任意一个 Object 类型的实例上调用。

练习题

isNaN(parseInt(new Date())) + Number([1]) + typeof undefined; //NaNundefined true
//parseInt(new Date()) -> NaN 
//parseInt("字符串");
// let time = new Date()
// time[Symbol.toPrimitive] -> ƒ [Symbol.toPrimitive]() { [native code] }
// time[Symbol.toPrimitive]('string')
// "Wed Jul 28 2021 09:23:56 GMT+0800 (中国标准时间)"
//NaNundefined

!(!"Number(undefined)"); //true
//Number(undefined) -> NaN/Number(null) -> NaN

parseFloat("1.6px") + parseInt("1.2px") + typeof parseInt(null); // 2.6number
//parseInt(null) -> parseInt("null") => NaN

Boolean(Number("")) + !isNaN(Number(null)) + Boolean("parseInt([])") + typeof !(null);
//空字符串变为0 --0
//parseInt([]) -> NaN =>"NaN"
//Boolean("parseInt([])") => true
//false + true + true + "boolean" -> "1boolean"

isNaN(Number(!!Number(parseInt("0.8")))); //false

!typeof parseFloat("0"); //false

Number(""); //0

16 + {}; //"16[object Object]"

{} + 16; //16特殊

var n = 6;
console.log((n++) + n-- + 5 + --n + --n);
console.log(n); //获取的n跟之前的计算有关系
//+/-号在前,先赋值后计算; +/-号在后,先计算后赋值
//6 + 7 + 5 + 5 + 4
//4

var str = 'abc123';
var num = parseInt(str);
if (num == NaN) {
    alert(NaN);
} else if (num == 123) {
    alert(123);
} else if (typeof num == 'number') {
    alert('number');
} else {
    alert('str');
}
//alert('number');NaN是属于数字类型

if (isNaN(NaN) == "") {
    console.log("珠峰")
} else {
    console.log("培训")
}
//isNaN(NaN); true -> 1
//"" -> 0
//培训