从类型转换深层讲解[ ] ==![ ] True or False

166 阅读7分钟

前言

面试中如果遇到  [ ] == ![ ]这类表达式题目,你可能会淡淡一笑。什么破题目还想考我,这能不是false我把桌子吃掉,然后面试官对你嘿嘿一笑:"你可以回去了" ,摸不着头脑的你回去一查,居然是true别看这短短的表达式却实则深藏玄机,别着急,看完这篇文章我相信你会有答案!

== vs ===

===

1 === 1     true                   // 不发生隐式类型转换

1 === '1'   false                                                 

=== 不会发生隐式类型转换,意味着会判断值和类型是否相等

==

1 == '1'   true                   //发生隐式类型转换

v8引擎看到这段代码,说不对,你一个数字,一个字符串让怎么比较,你非要比是吧,那行 我把二者转换成一样的东西来比较,它就把'1'转换成了数字1,也就是发生了隐式转换。

[] == []   false

我们知道,引用类型存储的是对象在内存中的引用地址,正所谓一山不容二虎,没有两个数组的地址是一样的,因此两个空数组也是false

我们提及到了隐式类型转换,接下来我们就来从原始类型对象转原始类型两个方面开始了解隐式转换


原始类型之间的转换

有的小伙伴就会问了:为什么隐式转换不会把原始类型转换成对象呢。放着简单的原始类型不弄 ,给自己找事情是吧,v8引擎真的会给你邦邦两拳

f8c56c12b9484a0a52623bc47f79f779.jpeg 因此原始类型的转换只有三种情况 1. 转布尔2. 转数字3. 转字符串

转布尔

//无效数字都是false
console.log(Boolean(0));//false
console.log(Boolean(1));//true
console.log(Boolean(-1));//true
console.log(Boolean(undefined));//false
console.log(Boolean(null));//false
console.log(Boolean(NaN));//false

//除空字符串外,其他都是true
console.log(Boolean('0'));//true
console.log(Boolean(''));//false

数字转布尔 只要是非零和非无效数字数字就是true

字符串转布尔 除了空字符串都是true

布尔转布尔 原封不动

转数字

console.log(Number('1'))   //1
console.log(Number(''))   //0
console.log(Number('0'))   //0
console.log(Number('hello')) //NaN 


console.log(Number(true)) //1
console.log(Number(false))  //0

console.log(Number(undefined))  //NaN
console.log(Number(null))  //0
console.log(Number(1234))  //1234
console.log(Number(NaN));   //NaN

数字转数字 1. undefined和NaN变成NaN 2.null最为特殊变成 0

字符串转数字 1.空字符串变成0
2.''内部是数字变成内部数字 3.''内部有除了数字以外的内容变成NaN

布尔转数字 t1f0

转字符串

最偷懒的一集,传的是啥就给它加个‘ ’

对象转原始类型

同样对象转原始类型有三种情况1. 转布尔2. 转数字3. 转字符串

转布尔

引用类型转布尔永远都是true(牢记!!!)

转字符串

方法:用String()或者Object.prototype.toString()二者效果一样

let a = {
  c: 1
}
let b = {}
console.log(String(a), String(b)) //[object Object] [object Object]

对象转字符串 返回由'[object '和 class 和']'组成的字符串 [object Object]

let a = [1, 2, 3]
let b = []

console.log(String(a))   //'1,2,3'
console.log(String(b))   // ''

数组转字符串 返回由所有元素以逗号拼接的字符串

function a() {
  '完蛋我要被转换了'
}
console.log(String(a))
//输出;
//function a() {
// '完蛋我要被转换了'
//}

其他引用类型转字符串 直接返回xx的字符串字面量,也就是把整个xx变成字符串再给你

在这里我要提到一个知识点ToPrimitive是它帮助我们将这些引用类型转成原始类型的。

我来解释一下这里,v8中的String(obj)想要把这个对象转换成字符串,发现搞不定,就去找它的上司ToPrimitive传给它这个对象obj的类型(第一个参数),和要转换的类型(第二个参数),让他来帮忙转换成自己String(obj)能处理的形式。

ToPrimitive(obj,String)

  • 如果obj是原始类型,直接返回
  • 否则 调用toString(),如果得到 原始类型,则返回([]会用自身的toString)
  • 否则 调用valueOf(),如果得到 原始类型 ,则返回
  • 否则 报错

因此我们要牢记引用类型转换成字符串后的输出

对象转字符串 返回由'[object '和 class 和']'组成的字符串 [object Object]

数组转字符串 返回由所有元素以逗号拼接的字符串

其他引用类型转字符串 直接返回xx的字符串字面量,也就是把整个xx变成字符串再给你

转数字

方法: Number(obj) 逻辑: ToNumber(obj) = > ToPrimitive(obj,Number)

(ToNumber(obj)这个T是大写的说明我们无法使用)

同样的,v8中的Number(obj)想要把这个对象转换成数字,发现搞不定,就去找ToPrimitive传给它这个对象obj的类型,说我要把obj转成数字型,让他来帮忙转换成自己能处理的原始类型,注意这里的ToPrimitive执行方法和对象转字符串还不太一样,ToPrimitive主打一个对症下药效率高

ToPrimitive(obj,Number)

  • 如果obj是原始类型,直接返回
  • 否则 调用valueOf(),如果得到 原始类型 ,则返回 //搞不定[]、{} 能搞定包装类
  • 否则 调用toString(),如果得到 原始类型,则返回([]会用自身的toString)
  • 否则 报错
let arr = [1, 2, 3];
console.log(arr.valueOf());// 输出:[1, 2, 3] 
console.log(arr); // 同样输出:[1, 2, 3]

let obj = { name: "Alice" };
console.log(obj.valueOf()); // 输出:{ name: "Alice" } 
console.log(obj); // 同样输出:{ name: "Alice" }

包装类
let a = new String('123') 
console.log(obj.valueOf()); // 输出:'123'

valueOf()他很神奇,它搞不定[]、{}等引用类型,只能将它直接返回,不能让他变成原始类型,但是它对包装类(包装类通常是指为基本数据类型提供对象封装的机制)有效果,可以将他变成原始类型。

如果valueOf()搞不定就会给toString()参照上面【转字符串】,如果得到原始类型,则返回如果还是不行,那它没办法喽,那就报错

隐式类型转换的场景

说了这么多隐式转换的规则,想必你已经想大展身手了把,我们来看看哪里能用到隐式转换

  1. 四则运算 + - * / %

  2. 判断语句 if while == !== > < >= <== (===全等不发生隐式类型转换)

我们来重点看看这个加号(+)

一元运算符 +
+ '1'    //1
+[  ]      //0      []经过String()变成 ''   '' ToNumber()后是0
+[1, 2]  //NaN  [1,2]经过String() 变成1,2  ToNumber()无法识别, 即为NaN

当他是一元运算符时,也就是加号左边时没有东西,右边有东西的情况,他会对右边的兄弟做出以下行为:

ToNumber(obj)

ToPrimitive(obj,Number)

如果是原始类型就照着原始类型转数字的规则转,否则先调用valueOf(),搞不定再调用String(),再搞不定我直接给你来个报错

008nviXoly1hl1bbas4jxj308c0840sw.jpg

二元运算符 +

val1 + val2

lprim = ToPrimitive(val1)

rprim = ToPrimitive(val2)

  • 如果lprim或rprim中有一个是字符串,另一个值直接 ToString()
  • 否则,返回ToNumber(lprim) 和 ToNumber(rprim) 相加的结果

举个🌰

null + 1    //1

他们先去调用 ToPrimitive,由于都是原始类型就都直接返回,lprim和rprim没有一个是字符串,就把他们都转成数字型 ,在数字转数字中,null最为特殊转变成0 , 变成了0+1,因此最后结果是1

==

我在这里放个传送锚点,篇幅问题,感兴趣的可以看es5.github.io/#x11.9.3

image.png

[ ] == ![ ]

学了这么多,我就是为了看懂你!

[ ] == ![ ]
[ ] == !true   //有!先将[ ]转化成布尔类型
[ ] == false   //取反
[ ] == 0       //我们看到== 规则表里面的 7 我们将 false转化为数字 也就是0
' ' == 0       //规则表 9 将 [ ]使用ToPrimitive的流程,经过流程里的String()后变成 ' '
 0 == 0       //规则表 5 将 ' '转变成数字,变成了0
true

欢迎大家的交流与指正看到这里了,就不妨动动手点个赞吧,谢谢大家

b54746051cf1b30f96b45df7d6e62842 (1).jpeg