原始值到原始值的转换

原始值转化为布尔值

所有的假值(undefined、null、0、-0、NaN、””)会被转化为 false,其他都会被转为 true

原始值转化为字符串 都相当于 原始值 + “”

原始值转为数字

  • 布尔转数字:true -> 1, false -> 0
  • 字符串转数字:以数字表示的字符串可以直接会转为字符串,如果字符串头尾有空格会忽略,但是空格在中间,转换结果就是 NaN。
  1. +" 66" // 66
  2. +" 6 7 " // NaN

原始值到对象的转换

  • null 和 undefined 转对象直接抛异常
  • 原始值通过调用 String()、Number()、Boolean()构造函数,转换为他们各自的包装对象
  • 类型转换 - 图1

对象到原始值的转换

对象转为布尔都为 true

对象到字符串

  • 如果对象有 toString() 方法,就调用 toString() 方法。如果该方法返回原始值,就讲这个值转化为字符串。
  • 如果对象没有 toString() 方法或者 该方法返回的不是原始值,就会调用该对象的 valueOf() 方法。如果存在就调用这个方法,如果返回值是原始值,就转化为字符串。
  • 否则就报错

对象到数字

  • 对象转化为数字做了跟对象转化为字符串做了想同的事儿,不同的是后者是先调用 valueOf 方法,如果调用失败或者返回不是原始值,就调用 toString 方法。

补充。一些常用内置对象 toString 方法和 valueOf 的转换规则

  • toString 相关
    类型转换 - 图2

  • valueOf 相关
    类型转换 - 图3

== 运算符如何进行类型转换

  1. 如果一个值是null,另一个值是undefined,则相等
  2. 如果一个是字符串,另一个值是数字,则把字符串转换成数字,进行比较
  3. 如果任意值是true,则把true转换成1再进行比较;如果任意值是false,则把false转换成0再进行比较
  4. 如果一个是对象,另一个是数值或字符串,把对象转换成基础类型的值再比较。对象转换成基础类型,利用它的 toString 或者 valueOf 方法。 js 核心内置类,会尝试 valueOf 先于 toString(可以理解为对象优先转换成数字);例外的是 Date,Date 利用的是 toString 转换。非 js 核心的对象,通过自己的实现中定义的方法转换成原始值。
  5. 类型转换 - 图4

+ 运算符如何进行类型转化

如果作为一元运算符就是转化为数字,常常用来将字符串转化为数字

  1. +"2" // 2
  2. 2+false // 0
  3. 复制代码

如果作为二元运算符就有两种转换方式

  • 两边如果有字符串,另一边一会转化为字符串进行相加
  • 如果没有字符串,两边都会转化为数字进行相加,对象也根据前面的方法转化为原始值数字。
  • 如果其中的一个操作数是对象,则将对象转换成原始值,日期对象会通过 toString() 方法进行转换,其他对象通过 valueOf()方法进行转换,但是大多数方法都是不具备可用的 valueOf() 方法,所以还是会通过 toString() 方法执行转换。

流程图如下:

类型转换 - 图5

+、==、||、&&

+ (重点)

  1. 1 + "1" // '11'
  2. 1 + "string" // "1tring" (加非数字字符串)
  3. 1 + true // 2
  4. 1 + false //1
  5. 1 + [] // "1"
  6. 1 + [1,2,3] // "11,2,3"
  7. 1 + {} // "1[object Object]"
  8. 1 + null // 1
  9. 1 + undefined // 1
  10. null + undefined // NaN
  11. true + 1 // 2
  12. true + "1" // "true1"
  13. true + "true" // "truetrue" (加非数字字符串)
  14. true + true // 2
  15. true + false // 1
  16. true + [] // "true"
  17. true + [1,2,3] // "true1,2,3"
  18. true + "true[object Object]" // NaN
  19. true + null // 1
  20. true + undefined // NaN
  21. false + 1 // 1
  22. false + "1" // "false1"
  23. false + "string" // "falsestring" (加非数字字符串)
  24. false + false // 0
  25. false + true // 1
  26. false + [] // "false"
  27. false + {} // "false[object Object]"
  28. [] + 1 // "1"
  29. [] + "1" // "1"
  30. [] + "string" // "string" (加非数字字符串)
  31. [] + true // "true"
  32. [] + false // "false"
  33. [] + [] // ""
  34. [1] + [1] // "11"
  35. [] + {} // "[object Object]" (注意!!!)
  36. [] + null // "null"
  37. [] + undefined // "undefined"
  38. {} + 1 // 1
  39. {} + "1" // 1
  40. {} + "string" // NaN
  41. {} + true // 1
  42. {} + false // 0
  43. {} + [] // 0 (注意!!!)
  44. { a:1 } + [] // 0(注意!!!)
  45. {} + [1] // 1 (注意!!!)
  46. {} + [1,2,3] // NaN (注意!!!)
  47. {} + {} // "[object Object][object Object]"
  48. {} + null // 0
  49. {} + undefined // NaN
  50. -Infinity + Infinity // NaN

总结:

  1. 如果某个操作数是字符串或者能够转换为字符串的话, + 将进行拼接操作。可见在 + 操作中字符串的拼接「优先级」高于数字的 +。
  2. 如果一个操作数是对象(包括数组),则首先对其调用ToPrimitive操作,该抽象操作再调用[[DefalutValue]],以数字作为上下文,保证了优先调用valueOf()方法,可见在 + 操作时,引用类型内部调用valueOf()方法的「优先级」高于toString()
  3. 数组或者对象在调用valueOf()方法如果无法获取到基本类型值(数字),会转而调用toString()
  1. // 这里提供valueOf()方法,
  2. var daRui = {
  3. valueOf: function() {
  4. return 18
  5. },
  6. toString: function() {
  7. return "daRUI"
  8. }
  9. }
  10. daRui + 7 // 25
  11. daRui + "7" // "187"
  12. daRui + "hello" // "18hello"
  13. // 这里仅提供toString()方法,
  14. var daRui = {
  15. toString: function() {
  16. return "daRUI"
  17. }
  18. }
  19. daRui + 7 // "daRUI7"
  20. daRui + " hello" // "daRUI hello"

特别说明:

  1. 数组中的toString()方法是经过改写的,故[1,2,3]会转为”1,2,3”,[] 会转为””。
  2. 对象的toString()方法会返回 “[object Class]”,Object.prototype.toString.call([]) 返回 “[object Array]”。
  3. [] + {}得到”[object Object]“,而{} + [] 得到 0。这是因为 {} (花括号)在JS中有两种含义:

    1. {} 在 + 后面时,表示对象{}
    2. {} 位于 + 前面时,{} 表示一个独立的「空代码块」,所以{} + [] 操作相当于进行的是 +[](一元操作符转换操作) 将 [] 转为0

==(重点)

  1. 1 == '1' // true
  2. 1 == true // true
  3. 1 == [] // false
  4. 1 == [1] // true
  5. 1 == {} // false
  6. true == "1" // true
  7. true == "true" // false
  8. true == [] // true
  9. true == [1] // true
  10. true == {} // false
  11. [] == "1"// false
  12. [] == true // false
  13. [] == false // true
  14. [] == [] // false (注意!!!)
  15. [] == {} // false
  16. [] == ![] // true (注意!!!)
  17. {} == 1 // Unexpected token '=='
  18. {} == "1" // Unexpected token '=='
  19. {} == true // Unexpected token '=='
  20. {} == false // Unexpected token '=='
  21. {} == [] // Unexpected token '=='
  22. {} == {} // false (注意!!!)
  23. "0" == null // false
  24. "0" == undefined // false
  25. "0" == false // true (注意!!!)
  26. "0" == NaN // false
  27. "0" == 0 // true
  28. "0" == "" // false
  29. false == null // false (注意!!!)
  30. false == undefined // false (注意!!!)
  31. false == NaN // false
  32. false == 0 // true (注意!!!)
  33. false == "" // true (注意!!!)
  34. false == [] // true (注意!!!)
  35. false == {} // false
  36. "" == null // false
  37. "" == undefined //false
  38. "" == NaN // false
  39. "" == 0 //true (注意!!!)
  40. "" == [] // true (注意!!!)
  41. "" == {} // false
  42. 0 == null // false
  43. 0 == undefined // false
  44. 0 == NaN // false
  45. 0 == [] // true (注意!!!)
  46. 0 == {} // false
  47. +0 == -0 // true
  48. null == null // true
  49. null == undefined // true
  50. null == "" // false
  51. null == 0 // false
  52. undefined == "" // false
  53. undefined == 0 // false
  54. NaN == NaN // false 可以使用isNaN() 判断是不是NaN

总结:

== 操作,最重要的时在两个操作数的转换过程!关于 == 操作ES5 规范11.9.3给出了明确规范:

  1. 字符串和数字之间的相等比较:

    1. 如果Type(x)是数字,Type(y)是字符串,则返回 x == ToNumber(y)的结果。
    2. 如果Type(x)是字符串,Type(y)是数字,则返回ToNumber(x) == y的结果。
  2. 其他类型值和布尔类型之间的相等比较:

    1. 如果Type(x)是布尔类型,这返回ToNumber(x) == y的结果
    2. 如果Type(y)是布尔类型,则返回x == ToNumber(y) 的结果
  3. null和undefined之间的相等比较

    1. 如果x为null,y为undefined,则结果为true
    2. 如果x为undefined,y为null,则结果为true
  4. 对象和非对象之间的相等比较:

    1. 如果Type(x)是字符串或者数字,Type(y)是对象,则返回x == ToPrimitive(y)的结果
    2. 如果Type(x)是对象,Type(y)是字符串或者数字,则返回toPrimitive(x) == y的结果
  5. 对象和对象之间比较:

    1. Javascript中对象属于引用类型,对象之间的比较,本质比较的是内存地址。所以都为false。
      (感谢掘友:Bug开发工程师同志 的指正)

== 相等操作中,如果两边的操作数不同的话,都会进行类型转换,而且优先转为数字,再进行比较,如果转换后还不同则再次转换,直到相同为止。这里以 字符串类型 == 布尔类型做介绍:

  1. 首先字符串类型转为Number 类型,这时比较的是 数字类型 == 布尔类型
  2. 再将布尔类型转为Number类型,这时比较的是数字类型 == 数字类型

这也就不难解释为什么"0" == false

如果连个操作数中有引用类型,这会先将引用类型转换为基本类型,在进行上面的操作,进行比较。

再说[] == ![] // true:

  1. 这里!操作的优先级是高于 == 的,![] 首先转换为false
  2. 此时比较双方是 [] == false,这里会将 [] 再次转换为 0
  3. 此时比较双方是 0 == false,接下来就不难理解了

!!

将值转为布尔值

  1. // 除将下面的转为false其余全部为true
  2. !! ""
  3. !! 0
  4. !! -0
  5. !! +0
  6. !! null
  7. !! undefined
  8. !! NaN