js类型转换之让人汗流浃背的面试题([] == ![])
前言
JavaScript中的类型转换是指将一个值从一种数据类型转换为另一种数据类型。这可以是隐式(自动)或显式(由程序员进行)。今天我们用一道面试题,细说一下js的几种转换类型。
面试题如下:
面试官问:这个输出false还是true? 答案是true.
第一次见到这样形式的运算比较,很自然的会认为这两边字符都不等,肯定输出false
,但是,既然面试官这样问了,我们肯定需要小心,不能自认为感觉是false
,细品你就知道这里输出true
。那么下面我们就简单介绍一些类型转换例子吧。
转换的类型
隐式类型转换:这是JavaScript自动进行的类型转换,通常在比较、运算符操作或函数调用时发生。
例如:
```
let x = "5";
let y = 2;
console.log(x + y); // 输出 "52",因为数字y被转换成字符串与x连接。
if(''){ //if里面也会进行隐式类型转换,由于if识别的是true和false,所以空字符串是向Boolean转换为false,不会进入if内部
console.log(123)
}
```
当出现对象类型转原始值时也被称为是隐式类型转换
-
对象转Number:
当出现像这样的Number({}),先调用ToNumber(x)方法规则如图,该函数中会调用ToPrimitive(),将对象转为原始值。
- ToPrimitive(obj,Number) 内部执行如下方法:
- 判断接收到的值是不是原始值类型,是则返回
- 否则,调用valueof()方法,如果得到了原始值,则返回
- 否则,调用toString方法,如果得到了原始值,则返回,
- 否则报错
-
对象转String:
一样的当出现String({}),JavaScript就先调用ToString(x),该函数中也会调用ToPrimitive()将对象转为原始值
- ToPrimitive(obj,String)
- 判断接收到的值是不是原始值类型,是则返回
- 否则,调用toString方法,如果得到了原始值,则返回
- 否则,调用valueOf方法,如果得到了原始值,则返回,
- 否则报错
这两个方法就是在ToPrimitive方法的内部2、3步骤调换顺序。
-
对象转布尔: 任何对象转布尔都是true。
-
由一元操作符触发的隐式转换
console.log(+[]) // 0
console.log(+{}) // NaN
在需要转换的数据类型前加一个运算符+
,触发隐式类型转换,因为JavaScript会将其看成运算操作,这样,后面的类型就需要向Number去转换,一样的操作,数组[]
和对象{}
都是对象对象类型,他们都不会返回原始值,都会调用toString方法,而数组的隐式原型上存在一个toString方法,使其转换为''
空字符串,空字符串转换为number0
,所以输出0
。
但是在{}
的隐式原型上不存在toString方法,他会继承Object显式原型上的toString方法,也就是Object.prototype.toString()。
下面是Object.prototype.toString()详解:
- 如果toString接受的值是 undefined,则返回"[[object Undefined]]"。
- 如果 toString 接受的值是null ,则返回"[object Null]"
- 调用 ToObject(X) 将X转为对象,此时得到的对象 内部一定拥有一个属性[[class]],而该属性[[class]]的值就是 X 的类型
- 设,class是[[class]]的值
- 返回由"[object " 和 class 和"]" 拼接得到字符串
当调用该方法时ToPrimitive(obj,Number)
会返回一个字符串"[object Object]",然后让字符串进行Number类型转换,它是一个原始数据类型,字符串除了空字符串转number类型是0
,任何带有字符的字符串都为NaN
,所以这里输出的是NaN
。
显式类型转换:这是程序员明确执行的类型转换,使用特定的方法或构造函数。以下均为基本数据类型的转换
-
Number() / parseInt() / parseFloat() 这些函数用于将其他类型的值转换为数字。
let str = "100"; let num = Number(str); // 或者使用parseInt()或parseFloat() console.log(num); // 输出 100
-
String() :将任何类型的值转换为字符串。
let num = 100; let str = String(num); console.log(str); // 输出 "100"
-
Boolean() :将任何类型的值转换为布尔值。
let str = "Hello"; let bool = Boolean(str); console.log(bool); // 输出 true,除非str是false、null、0、""、undefined或NaN,否则结果总是true。
类型强制:这是通过赋值操作或比较操作发生的隐式类型转换。
-
赋值类型强制: 当你将一个值赋给一个不同类型的变量时,可能会发生类型转换。
let x = "42"; // 字符串 let y = x; // y也是字符串,没有类型转换 let z = Number(y); // 显式转换为数字
-
比较类型强制: 在比较操作中,如果两个操作数类型不同,JavaScript会尝试将它们转换为相同的类型。
let x = "5"; let y = 5; console.log(x == y); // 输出 true,因为字符串"x"被转换为数字然后与数字y比较。==会触发隐式类型转换 console.log(x === y); //输出 false, 因为 === 不会触发隐式类型转换,单纯的比较值是否相等
面试题分析
console.log([]==![])
由于该类型的比较涉及到多次的类型转换,我们可以分步进行分析:
右边表达式![]
存在感叹号,在JavaScript中,!
(逻辑非)是一个一元运算符,用于对单个操作数求反。当作用于布尔值时,它的行为很简单:将true
转换为false
,将false
转换为true
。其优先级更高,须先处理。
- 由于空数组
[]
是一个对象,对象转换为布尔类型都为true
,!对true
取反,所以![]
被处理为false
。
再看左边表达式:一个单纯的[]
空数组,并没有上面特殊,所以直接开始比较。表达式可以看为[] == false
。
-
当进行比较操作时:两边均往number转(会去调用ToNumber(x)),由于右边为布尔类型,属于基本类型,我们知道
0
转为布尔是false
,1
转为布尔是true
。所以右边变为了0
。 -
现在就成了
[] == 0
的比较,[]
对象的转换我们参照上面聊的,对象向Number类型的转换,JavaScript会去调用toNumber()方法,它发现是对象类型的转换,就会去找ToPrimitive()帮忙,执行如下步骤,alueOf
方法通常用于将对象转换为其原始值,如果valueOf
返回一个原始值,那么这个值将被使用;如果返回的还是一个对象,JavaScript引擎会继续尝试调用toString
方法。这里的
[]
调用后还是返回一个对象,所以接下来就是调用toString
方法,而数组本身就存在一个toString
方法。他会将[]
转换为空字符串''
。ToPrimitive()返回一个空字符串,空字符串转化为number类型就是0
了。
所以console.log([]==![]) 输出 true
。
结尾
这里就不说什么了,我们附上一张类型转换表吧!
转载自:https://juejin.cn/post/7379792320156237864