zoukankan      html  css  js  c++  java
  • JavaScript类型转换总结与常见情况解析

    类型转换是将值从一种类型转换为另一种类型的过程(比如字符串转数字,对象转布尔值等)

    一、类型转换的分类

    类型转换可以分为隐式类型转换和显式类型转换。

    二者的区别显而易见:我们能够从代码中看出哪些地方是显式强制类型转换,而隐式强制类型转换则不那么明显,通常是某些操作产生的副作用。

    例如:

    var a = 42;
    
    var b = a + "";       // 隐式强制类型转换
    
    var c = String( a );  // 显式强制类型转换

    对变量 b 而言,强制类型转换是隐式的;由于 + 运算符的其中一个操作数是字符串,所以是字符串拼接操作,结果是数字 42 被强制类型转换为相应的字符串”42“。

    而 String() 则是将 a 显示强制类型转换为字符串。

    严格相等运算符(===)不会触发类型隐式转换,所以它可以用来比较值和类型是否都相等。

    隐式类型转换是一把双刃剑,使用它虽然可以写更少的代码但有时候会出现难以被发现的bug。

    二、类型转换分析

    1、Boolean 类型转换

    在条件判断时,除了 undefinednullfalseNaN' '0-0,其它所有值都转为 true,包括所有对象。

    Boolean() 方法可以用来显式将值转换成 boolean 型。

    隐式类型转换通常在逻辑判断或者有逻辑运算符时被触发(|| && !)

    Boolean(2)     // 显示类型转换
    if(2) {}          // 逻辑判断触发隐式类型转换
    !!2                 // 逻辑运算符触发隐式类型转换
    2 || 'hello'      // 逻辑运算符触发隐式类型转换

    2、String 类型转换

    String() 方法可以用来显式将值转为字符串,隐式转换通常在有 + 运算符并且有一个操作数是 string 类型时被触发,如:

    String(123) // 显式类型转换
    
    123 + ''      // 隐式类型转换

    Symbol 类型转 String 类型是比较严格的,它只能被显式的转换

    String(Symbol('symbol'))  // 'Symbol(symbol)'
    
    '' + Symbol('symbol')       // TypeError is thrown

     

    3、Number 类型转换

    Boolean()String() 方法一样, Number() 方法可以用来显式将值转换成 number 类型。

    number 的隐式类型转换是比较复杂的,因为它可以在下面多种情况下被触发。

    • 比较操作(>, <, <=, >=)
    • 按位操作(| & ^ ~)
    • 算数操作(- + * / %)--- 注意:当 + 操作存在任意的操作数是 string 类型时,不会触发 number 类型的隐式转换
    • 一 元 + 操作
    • 非严格相等操作(== 或者 !== )--- 注意:== 操作两个操作数都是 string 类型时,不会发生 number 类型的隐式转换
    Number('123')    // 显示类型转换
    + '123'              //  隐式类型转换
    123 != "456"     //  隐式类型转换
    4 > "5"             //  隐式类型转换
    5 / null             //  隐式类型转换
    true | 0            //  隐式类型转换

    这里有 2 个特殊的规则需要记住:

    • 当将 == 应用于 null 或 undefined 时,不会发生数值转换。null 只等于 null 或 undefined,不等于其他任何值。
    null == 0                       // false, null is not converted to 0
    null == null                    // true
    undefined == undefined  // true
    null == undefined           // true
    undefined == 0              // false
    • NaN 不等于任何值,包括它本身
    NaN === NaN      // false

    4、object 类型转换

    到这里我们已经深入了解了原始类型的转换,接下来我们来看一下对象转原始类型。

    对象在转换类型时,会调用内置的 [[ToPrimitive]]  函数,对于该函数来说,算法逻辑一般如下:

    • 如果已经是原始类型了,那就不需要转换了
    • 如果需要转字符串类型就调用 x.toString(),结果为基础类型则返回转换的值;非字符串类型则先调用 valueOf,结果非基础类型再调用 toString
    • 调用 x.valueOf() ,如果转换为基础类型,则返回转换的值
    • 如果都没有返回原始类型,就会报错

    当然也可以重写 Symbol.toPrimitive,该方法在转原始类型时调用优先级最高:

    let a = {
      valueOf() {
        return 0
      },
      toString() {
        return '1'
      },
      [Symbol.toPrimitive]() {
        return 2
      }
    }
    1 + a     // => 3

    5、四则运算符

    加法运算符不同于其他几个运算符,它有以下两个特点:

    • 特点一:运算中其中一方为字符串,那么就会把另一方也转换为字符串
    • 特点二:如果一方不是字符串或者数字,那么会将它转换为数字或者字符串
    1 + '1'            // '11'
    true + true     // 2
    4 + [1,2,3]    // "41,2,3"

    如果你对于答案有疑问的话,请看解析:

    • 对于第一行代码来说,触发特点一,所以将数字 1 转换为字符串,得到结果 '11'
    • 对于第二行代码来说,触发特点二,所以将 true 转为数字 1
    • 对于第三行代码来说,触发特点二,所以将数组通过 toString 转为字符串 1,2,3,得到结果 41,2,3

    另外对于加法还需要注意这个表达式 'a' + + 'b'

    'a' + + 'b'     // -> "aNaN"

    因为 + 'b' 等于 NaN,所以结果为 "aNaN",你可能也会在一些代码中看到过 + '1' 的形式来快速获取 number 类型。

    那么对于除了加法的运算符来说,只要其中一方是数字,那么另一方就会被转为数字

    4 * '3'         // 12
    4 * []         // 0
    4 * [1, 2]   // NaN

     

    6、比较运算符

    • 如果是对象,就通过 toPrimitive 转换对象
    • 如果是字符串,就通过 unicode 字符索引来比较
    let a = {
      valueOf() {
        return 0
      },
      toString() {
        return '1'
      }
    }
    a > -1     // true

    在以上代码中,因为 a 是对象,所以会通过 valueOf 转换为原始类型再比较值。

    7、== VS ===

    对于 == 来说,如果对比双方的类型不一样的话,就会进行类型转换,流程如下:

    (1)首先判断两者类型是否相同。相同的话就是比大小了

    (2)类型不相同的话,则进行类型转换

    (3)会先判断是否在对比 null 和 undefined,是的话就会返回 true

    (4)判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number

    1 == '1'1 ==  1

    (5)判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断

    '1' == true'1' ==  11  ==  1

    (6)判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断

    '1' == { name: 'yck' }
                    ↓
    '1' == '[object Object]'

    对于 === 来说就简单多了,就是判断两者类型和值是否相同

    三、18 种常见情况解析

    1、常见情况

    true + false     // 1

    '+' 运算符会触发 number 类型转换对于 true 和 false

    2、常见情况

    12 / '6'     // 2

    算数运算符会把字符串 ‘6’ 转为 number 类型

    3、常见情况

    "number" + 15 + 3        // "number153"

    '+' 运算符按从左到右的顺序的执行,所以优先执行 “number” + 15, 把 15 转为 string 类型,得到 “number15” 然后同理执行 “number15” + 3

    4、常见情况

    15 + 3 + "number"     // "18number"

    15 + 3 先执行,运算符两边都是 number 类型 ,不用转换,然后执行 18 + “number” 最终得到 “18number”

     

    5、常见情况

    [1] > null  // true
    
    ==> '1' > 0
    ==> 1 > 0
    ==> true

    比较运算符 > 执行 number 类型隐式转换。

    6、常见情况

    "foo" + + "bar"  // "fooNaN"
    
    ==> "foo" + (+"bar")
    ==> "foo" + NaN
    ==> "fooNaN"

    一元 + 运算符比二元 + 运算符具有更高的优先级。所以 + bar表达式先求值。一元加号执行字符串“bar” 的 number 类型转换。因为字符串不代表一个有效的数字,所以结果是NaN。在第二步中,计算表达式'foo' + NaN

    7/8、常见情况

    'true' == true       // false
    
    ==> NaN == 1
    ==> false
    
    'false' == false     // false
    
    ==> NaN == 0
    ==> false

    == 运算符执行 number 类型转换,'true' 转换为 NaN, boolean 类型 true 转换为 1

    9、常见情况

    null == '' // false

    null 不等于任何值除了 null 和 undefined

    10、常见情况

    !!"false" == !!"true"  // true
    
    ==> true == true
    ==> true

    !! 运算符将字符串 'true' 和 'false' 转为 boolean 类型 true, 因为不是空字符串,然后两边都是 boolean 型不在执行隐式转换操作。

    11、常见情况

    ['x'] == 'x'     // true

    == 运算符对数组类型执行 number 转换,先调用对象的 valueOf() 方法,结果是数组本身,不是原始类型值,所以执行对象的 toString() 方法,得到字符串 'x'

    12、常见情况

    [] + null + 1      // 'null1'
    
    ==> '' + null + 1
    ==> 'null' + 1
    ==> 'null1'

    '+' 运算符执行 number 类型转换,先调用对象的 valueOf() 方法,结果是数组本身,不是原始类型值,所以执行对象的 toString() 方法,得到字符串 '', 接下来执行表达式 '' + null + 1

    13、常见情况

    0 || "0" && {}      // {}
    
    ==> (0 || '0') && {}
    ==> (false || true) && true
    ==> true && true
    ==> true

    逻辑运算符 || 和 && 将值转为 boolean 型,但是会返回原始值(不是 boolean)

    14、常见情况

    [1,2,3] == [1,2,3]     // false

    当运算符两边类型相同时,不会执行类型转换,两个数组的内存地址不一样,所以返回 false

    15、常见情况

    {} + [] + {} + [1]      // '0[object Object]1'
    
    ==> +[] + {} + [1]
    ==> 0 + {} + [1]
    ==> 0 + '[object Object]' + '1'
    ==> '0[object Object]1'

    所有的操作数都不是原始类型,所以会按照从左到右的顺序执行 number 类型的隐式转换, object 和 array 类型的 valueOf() 方法返回它们本身,所以直接忽略,执行 toString() 方法。 这里的技巧是,第一个 {} 不被视为 object,而是块声明语句,因此它被忽略。计算从 +[] 表达式开始,该表达式通过toString()方法转换为空字符串,然后转换为0

    16、常见情况

    ! + [] + [] + ![]     // 'truefalse'
    
    ==> !(+[]) + [] + (![])
    ==> !0 + [] + false
    ==> true + [] + false
    ==> true + '' + false
    ==> 'truefalse'

    一元运算符优先执行,+[] 转为 number 类型 0,![] 转为 boolean 型 false

    17、常见情况

    new Date(0) - 0      // 0
    
    ==> 0 - 0
    ==> 0

    '-' 运算符执行 number 类型隐式转换对于 Date 型的值,Date.valueOf() 返回到毫秒的时间戳

    18、常见情况

    new Date(0) + 0        // 'Thu Jan 01 1970 02:00:00 GMT+0200 (EET)0'
    
    ==> 'Thu Jan 01 1970 02:00:00 GMT+0200 (EET)' + 0
    ==> 'Thu Jan 01 1970 02:00:00 GMT+0200 (EET)0'

    '+' 运算符触发默认转换,因此使用 toString() 方法,而不是 valueOf()

  • 相关阅读:
    实例15_C语言绘制万年历
    医生酒精
    实例13_求解二维数组的最大元素和最小元素
    用二维数组实现矩阵转置
    C语言中的typedef跟define的区别
    C语言设计ATM存取款界面
    MyBatis,动态传入表名,字段名的解决办法
    在mybatis执行SQL语句之前进行拦击处理
    使用Eclipse构建Maven的SpringMVC项目
    Debug过程中的mock (及display窗口的使用)
  • 原文地址:https://www.cnblogs.com/Leophen/p/11384511.html
Copyright © 2011-2022 走看看