一些怪癖的类型转换

收集了一些比较怪的类型转换,我不懂原因,大家讨论讨论这是为什么?

[] == ![]         // true
'1' == true       // true
'2' == true       // false
'3' == true       // false
null >= 0         // true
{} + 1            // 1
{var a = 10} + 1  // 1
{} + {}           // "[object Object][object Object]"
+[]               // 0
{} + []           // 0
[1,2]+ [3,4]      // "1,23,4"
+[2]              // 2
[2]+1             //"21"
[2] + (-1)        // "2-1"

null特殊情况

+null        // 0
null == 0    // false
null > 0     // false
null < 0     // false

!!null         // false
null == false  // false
null == true   // false
阅读 2.6k
4 个回答

使用 == 进行比较,如果比较的类型不同,可以分为四种情况
1. 字符串与数字之间的相等比较,此时 字符串会被强制转换为数字
2. 其他类型和布尔值类型之间的相等比较, 布尔值会被强制转换为数字, true转为1,false 转为0
3. null 和undefined之间的相等比较, null == undefined 返回true
4. 对象和非对象之间的相等比较, 对象需要进行ToPrimitive抽象操作(如toString(), valueOf())

根据上面的四条规律,来理解下面几题:

  1. [] == ![] 相当于 [] == false 相当于 '' == false 相等于 '' == 0 相当于 0 == 0 因此结果为true
  2. '1' == true 相当于 '1' == 1 相当于 1==1 因此结果为true
  3. '2' == true 相当于 '2' == 1 相当于 2 ==1 因此结果为false
  4. '3' == true 相当于 '3' == 1 相当于 3 ==1 因此结果为false

根据规范 a<=b 会被当做 b < a,然后将结果反转,因此

  1. null >= 0 可以理解为 !(null < 0)。 因为 null < 0 为false, 所以 null >=0 结果为true

使用+操作符时,如果其中一个操作符是对象,那么会先调用valueOf()方法,如果该方法返回基本类型值,就不再调用toString()方法。否则就调用toString()方法(有个例外,Date 的实例对象总是先调用toString,再调用valueOf)。
数组的valueOf()得不到基本类型值,所以数组会调用toString()方法。

  1. {} + 1 的前面的{}是个空的代码块, 如果加上换车键 或许更好理解了。{} 回车 +1。 +1结果返回1了。
  2. {var a = 10} + 1 道理跟上面一下,前面的{}是个代码块,只是后面没加回车键。+1结果返回1.
  3. {} + {} 是两个空对象相加, 分为转为字符串({}).toString(),再相加。结果为"object Object"
  4. +[] 相当于 Number([]), 结果为0
  5. {} + [] 这里{}依旧是空代码块,因此返回+[]的结果,也就是0
  6. [1,2]+ [3,4] [1,2]转为字符串'1,2' [3,4]转为字符串'3,4' , 因此结果为 "1,23,4"
  7. +[2] 相当于 +'2' 相当于 Number('2')因此,结果为2
  8. [2]+1 相当于 '2' +1 因此,结果为'21'
  9. [2] + (-1) 相当于 '2'+(-1) 因此结果为'2-1'

[] == ![] => [] == false (数组取反) => '' == false ([]转原始值) => 0 === 0 (调用Number转换)
'1' == ture => 1 === 1 (调用Number)
'2' == true => 2 === 1 (调用Number)
'3' == true => 3 === 1 (调用Number)
null >= 0 => 0 >= 0 (调用Number)
{} + 1 => + 1 => 1 (前一个{}被认为是一个空代码块)
{ var a = 10 } + 1 => + 1 (类似上面 等价于 "+ 1")
{} + {} => [object Object] + [object Object] (左右两边被认为为对象 分别调用toString()并相加)

  • [] => + 0 (调用Number)

{} + [] => + [] ("{}" 被认为是空代码块) => + 0 (调用Number)
[1,2] + [3,4] => '1,2' + '3,4' (调用toString)

  • [2] => + 2 (调用Number)

[2] + 1 => '2' + 1 (先求原始值调用toString) => '21' (存在string 优先转化为string)
[2] + (-1) => '2' + -1 (先求原始值调用toString) => '2-1' (存在string 优先转化为string)

我刚学js的时候,这些地方也是啃啊啃啊。。

后来发现,在实际工作中,这些东西,知道当然好。

但是,实际项目中确是应该尽力避免开用'=='比较时候的隐式转换的。

基本上,只有真假类值的隐式转换用的比较多,if(a)这种。比较时候,都是'==='来比较的,避免'=='造成的意想不到的结果。

而且,现在更严格的项目,都要使用ts,强制性的使用类型声明,基本不会遇到比较中的隐式转换。

”孔乙己显出极高兴的样子,将两个指头的长指甲敲着柜台,点头说,“对呀对呀!……回字有四样写法,你知道么?”


别误人子弟了,天天研究这些没用的东西
另一个人已经说过了,在实际工程实践中除了真假值的隐式转换以外应避免使用其他的隐式转换,如果除了真假值的隐式转换以外还大量使用其他的隐式转换,那只会严重降低代码可读性、增加协作难度和bug率,还可能被同事殴打
在工作中写这种代码,说得难听点叫找抽


js的坑,知道在哪里,知道如何避开就足够了,不了解坑里面具体长什么样是完全可以的
对于初学者来说,在有足够的解决实际问题的编程抽象能力之前,不去提高自己的编程能力,反而天天对着各种语法细节丝扣,甚至去研究语言中坑的部分,完全是本末倒置,多花时间写代码实践解决实际问题才是最正确的方向(只要不蠢,语法什么的都是自然而然就能掌握的东西)

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题