一梦七年。
一派青春撞了南墙,一生热爱回头太难。
文章 20
标签 29
分类 11
JS 中的隐式类型转换

JS 中的隐式类型转换

JS 中的隐式类型转换

由于 JS 是动态类型语言,没有类型声明,因此 JS 内部一些运算中会将变量强制转换成相同类型后再进行运算。那么来看看 JS 中都有哪些隐式转换吧!

一 、数学运算符中的隐式转换

  1. 减法、乘法、除法运算及 % 取余运算等数学类运算(包括内置的 Math 等 API),会将非 Number 类型的值转换成 Number 类型之后再进行运算。例如下面的计算:

    1 - ""; // => 1; "" 转换成数字 0, 1 - 0 = 1
    1 - true; // => 0; true 转换成数字 1, 1 - 1 = 0
    1 - null; // => 1; null 转换成数字 0, 1 - 0 = 1
    1 - undefined; // => NaN; undefine、NaN 转成数字 NaN, 1 -     NaN = NaN
    1 - {}; // => NaN; 无法转换成数字的其他类型先转换成数字 NaN,  1 - NaN = NaN
    
    // 数组类型会先被转成字符串,然后再将字符串转换成数字进行运
    1 - ["1"]; // => 0; ["1"] 转换成字符串 "1" ,字符串 "1" 转换成 数字 1 , 1 - 1 = 0
    1 - [undefined]; // => 1; [undefined]转换成字符串 "" ,字符串  "" 转换成数字 0, 1 - 0 = 1
    1 - [null]; // => 1; [null]转换成字符串 "" ,字符串 "" 转换成  数字 0, 1 - 0 = 1
    1 - [NaN]; // => NaN; [NaN]转换成字符串 "NaN" ,字符串 "NaN"   转成数字 NaN, 1 - NaN = NaN
    1 - [1, 2]; // => NaN; [1, 2]转换成字符串 "1,2" ,字符串 "1,   2" 转成数字 NaN, 1 - NaN = NaN

    其中数组参与的计算中涉及到 JS 对象的装箱和拆箱概念(个人理解如:数组转成字符串即是拆箱,数字 1 转成 Number(1) 实例即是装箱),相关比较权威的讲解可以自行谷歌。

  2. 加法( + )的特殊隐式转换。由于 + 在 JS 中既可以作为数学运算符,又可以作为字符串拼接符,因此 + 涉及的隐式转换比其他数学运算符特殊一点。例如下面的运算:

    // 1、当加号一侧为 String 类型,加号会被识别为字符串拼接符,并优先将另一侧值转换成字符串类型
    123 + "123"; // => "123123"; 将 Number 类型的 123 转换成 String 类型 "123" ,再进行字符串拼接运算, "123" + "123" = "123123"
    "123" + {}; // => "123[object Object]"; 将空对象 {} 转换成 String 类型 "[object Object]" ,再进行字符串拼接运算, "123" + "[object Object]" = "123[object Object]"
    
    // 2、当加号一侧为 Number 类型,另一侧为其他非 String 类型的原始类型时,加号被识别为算数运算符,其他非 String 类型的原始类型将被转换成 Number 类型,然后进行算数运算。
    123 + true; // => 124; 将 true 转换成 1, 123 + 1 = 124
    123 + undefined; // => NaN; 将 undefined 转换成 NaN, 123 + NaN = NaN
    123 + null; // => 123; 将 null 转换成 0, 123 + 0 = 123
    
    // 3、当加号一侧为 Number 类型,另一侧为引用类型时,会将 Number 类型和引用类型转换成字符串,然后执行字符串拼接操作
    123 + {}; // => "123[object Object]"; 将 123 转换成 String 类型 "123",将空对象 {} 转换成 String 类型 "[object Object]" ,再进行字符串拼接运算, "123" + "[object Object]" = "123[object Object]"
    123 + [1, 2, 3]; // => "1231,2,3"; 将 123 转换成 String 类型 "123",将数组 [1, 2, 3] 转换成 String 类型 "1,2,3" ,再进行字符串拼接运算, "123" + "1,2,3" = "1231,2,3"

    当然以上实例来源于网络资料,但都实际 console 过,所以答案应该是正确的。不过个人觉得第 3 点解析可能不准确,因为第 3 点解析应该基于第 1 点解析。拿 123 + {} 来说。首先,引用类型的值先转成字符串,即空对象 {} 转换成 String 类型 “[object Object]”, 此时算式变成 123 + "[object Object]", 然后再基于第一点来解析,即一侧为 String 类型的情况。个人感觉这样解释更为合理。

二 、逻辑语句中的隐式转换

  • 在如 if 、else if 、while 等逻辑语句中,预期的表达式结果是一个 Boolean 类型值,因此在这些逻辑语句的条件表达式中会存在其他类型值转换成 Boolean 类型值的隐式转换。
  1. 当表达式是单个变量或单独的一个值时,会将这个值转换成 Boolean 类型,如:null undefined '' NaN 0 false 会转换成 false,其他值如 {} [] 等会转换成 true

  2. 当表达式使用 == 进行比较时会存在几种隐式转换,大致有如下几条规则:

    // 1、当其中一个为 `Boolean` 类型值时,会先将 `Boolean` 类型值转换成 `Number` 类型,然后进行比较。
    true == 1; // => true; 先将 true 转换成 1, 1 == 1 为 true
    true == "1"; // => true; 先将 true 转换成 1, "1" 转换成 1, 1 == 1 为 true
    true == ["1"]; // => true; 先将 true 转换成 1, 然后将 ["1"] 拆箱转成 "1", "1" 转换成 1, 1 == 1 为 true
    true == ["1", "2"]; // => false; 先将 true 转换成 1, 然后将 ["1", "2"] 拆箱转成 "1,2", "1,2" 转换成 NaN, 1 == NaN 为 false
    false == undefined; // => false; 先将 false 转换 0, 0 == undefined 为 false
    false == null; // => false; 先将 false 转换 0, 0 == null 为 false
    
    // 2、当 `String` 类型值与 `Number` 类型值比较时,将 `String` 类型转换成 `Number` 类型,然后进行比较。
    "123" == 123; // => true; 将 "123" 转换成 123, 123 == 123 为 true
    "" == 0; // => true; 将 "" 转换成 0, 0 == 0 为 true
    
    // 3、undefined == undefined、null == null、undefined == null 为 true,但是 undefined 或者 null 与其他任何值比较结果都为 false
    undefined == 0; // => false
    null == 0; // => false
    undefined == ""; // => false
    null == ""; // => false
    
    // 3、原始类型和引用类型比较时会按照 ToPrimitive 规则来将引用类型转换成原始类型,如果 Obj[Symbol.toPrimitive](hint) 方法存在,则调用该方法进行转换。否则,如果 hint 值为 "number" 或者 "default",则优先调用 valueOf() 方法转换成原始值,如果没有 valueOf() 或者得到的不是原始类型,再调用 toString() 方法。如果 hint 值为 "string",则先尝试调用 toString() 方法,没有或者得到的值不是原始类型,则调用 valueOf() 方法。则抛出异常 Uncaught TypeError: Cannot convert object to primitive value
    "[object Object]" == {}; // => true; 由于左侧是字符串,所以预期右侧要是个字符串,因此将 {} 转换成字符串 "[object Object]","[object Object]" == "[object Object]" 为 true
    1 == ["1"]; // => true; 左侧是 Number 类型,所以右侧也期望一个 Number 类型,因此将 ["1"] 转换成数字,但["1"]没有Symbol.toPrimitive属性对应的方法,且 valueOf() 返回的不是原始类型,所以调用 toString() 方法将["1"]转换成 "1", 再将 "1" 转换成 1,1 = 1 为 true

注意 :ToPrimitive 规则的详细及准确的定义请查看 JS 相关标准文档,本文中纯属个人根网络资料及实际代码运行得出的个人解析,只保证表达式结果正确(都运行过),不保解析正确!

本文引用 :