1. true + false
  2. 12 / "6"
  3. "number" + 15 + 3
  4. 15 + 3 + "number"
  5. [1] > null
  6. "foo" + + "bar"
  7. "true" == true
  8. false == "false"
  9. null == ""
  10. !!"false" == !!"true"
  11. ["x"] == "x"
  12. [] + null + 1
  13. 0 || "0" && {}
  14. [1,2,3] == [1,2,3]
  15. {} + [] + {} + [1]
  16. ! + [] + [] + ![]
  17. new Date(0) - 0
  18. new Date(0) + 0

答案解析

接下来我们按照之前的转换逻辑来解释一下每一道题,看一下是否和你的答案一样。

  1. true + false // 1

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

  1. 12 / '6' // 2

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

  1. "number" + 15 + 3 // "number153"

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

  1. 15 + 3 + "number" // "18number"

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

  1. [1] > null // true
  2. ==> '1' > 0
  3. ==> 1 > 0
  4. ==> true
  5. 复制代码

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

  1. "foo" + + "bar" // "fooNaN"
  2. ==> "foo" + (+"bar")
  3. ==> "foo" + NaN
  4. ==> "fooNaN"
  5. 复制代码

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

  1. 'true' == true // false
  2. ==> NaN == 1
  3. ==> false
  4. 'false' == false // false
  5. ==> NaN == 0
  6. ==> false
  7. 复制代码

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

  1. null == '' // false
  2. 复制代码

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

  1. !!"false" == !!"true" // true
  2. ==> true == true
  3. ==> true
  4. 复制代码

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

  1. ['x'] == 'x' // true
  2. 复制代码

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

  1. [] + null + 1 // 'null1'
  2. ==> '' + null + 1
  3. ==> 'null' + 1
  4. ==> 'null1'
  5. 复制代码

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

  1. 0 || "0" && {} // {}
  2. ==> (0 || '0') && {}
  3. ==> (false || true) && {}
  4. ==> true && {}
  5. ==> {}
  6. 复制代码

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

  1. [1,2,3] == [1,2,3] // false
  2. 复制代码

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

  1. {} + [] + {} + [1] // '0[object Object]1'
  2. ==> +[] + {} + [1]
  3. ==> 0 + {} + [1]
  4. ==> 0 + '[object Object]' + '1'
  5. ==> '0[object Object]1'
  6. 复制代码

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

  1. ! + [] + [] + ![] // 'truefalse'
  2. ==> !(+[]) + [] + (![])
  3. ==> !0 + [] + false
  4. ==> true + [] + false
  5. ==> true + '' + false
  6. ==> 'truefalse'
  7. 复制代码

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

  1. new Date(0) - 0 // 0
  2. ==> 0 - 0
  3. ==> 0
  4. 复制代码

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

  1. new Date(0) + 0
  2. ==> 'Thu Jan 01 1970 02:00:00 GMT+0200 (EET)' + 0
  3. ==> 'Thu Jan 01 1970 02:00:00 GMT+0200 (EET)0'

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