[]==![]竟然输出true?是一道常见的面试题,当你碰到的时候可能会蒙一个true,但是面试官就会接着问你为什么?这就需要深入理解JS中的类型转换机制了,本篇文章就让我们来看看JS中的类型转换是怎么转换的,看完相信你就可以清晰地回答这个问题了。
一. == VS ===
先来看下两个等号与三个等号的区别。
可以看出两个等号比的是数值,而三个等号比的是数值加类型,也就是说==会发生隐式转换,会隐式地将字符串'1'转换为数字1,然后数字1跟1比较,所以得出true,而===就不会发生隐式转换了,接下来就让我们来聊聊JS中类型转换机制。
二. 原始类型之间的转换
原始类型之间的转换一般只涉及到转布尔,转数字和转字符串。
2.1 转布尔
数字和字符串转布尔值,只要数字不为0,字符串不为空的话就是true,其它则是false。
console.log(Boolean(undefined)); //false
console.log(Boolean(null)); //false
console.log(Boolean(0)); //false
console.log(Boolean(NaN)); //false
console.log(Boolean('')); //false
console.log(Boolean(-100)); //true
console.log(Boolean(50)); //true
console.log(Boolean('hello')); //true
2.2 转数字
当字符串里面是个数字时,就能转成相应的数字,字符串为空时转成0,true转1,false转0,null转化0,undefined转成NaN,当然这些猜基本都能猜到。
console.log(Number('1')); //1
console.log(Number('0')); //0
console.log(Number('')); //0
console.log(Number('')); //0
console.log(Number(true)); //1
console.log(Number(false)); //0
console.log(Number(null)); //0
console.log(Number(undefined)); //NaN
console.log(Number('hello')); //Nan
console.log(Number(null)); //0
console.log(Number(undefined)); //NaN
console.log(Number('hello')); //Nan
2.3 转字符串
转字符串就不管是什么,直接加个''即可;
console.log(String(undefined)); //undefined
console.log(String(null)); //null
三. 引用类型转原始类型
3.1 引用类型转布尔
全部引用类型转布尔都是true,无论是空还是有属性。
3.2 引用类型转字符串
引用类型转字符串可以使用String(x)来表示其转化成字符串的值,实质上就是调用它们自己toString(x)方法,如下;
- 对于一个对象
{},它调用的是对象原型上的toString()方法(也可以用Object.prototype.toString()调用),所以会返回'[object' 和 class 和 ']'组成的字符串。 - 对于一个数组
[],它的构造函数上已经有了toString方法,就不会沿着原型链接着往上查找到对象原型上的方法了,就将里面每个元素用字符串方式输出。 - 对于其它,其对象原型上有其相应的
toString()方法,所以返回对应的字符串字面量。
3.3 引用类型转数字
对象转数字为NaN。
ToPrimitive
为什么当x是一个对象时,String(x)返回的是它的类型呢?而且这个与Object.prototype.toString(obj)的输出结果是一样的。
let obj = {
a: 1,
b: 2
}
console.log(String(obj)); //[object Object]
console.log(Object.prototype.toString(obj)); //[object Object]
那么就不得不提到当执行String(x)时,当x是一个对象时,V8 引擎会尝试将 x 转换为一个原始字符串值,这个过程涉及到 ToPrimitive 抽象操作(通常会在将对象转换为原始值(字符串,数字或布尔值)时用到)。它接收俩个参数,一个为需要转换的变量,一个为要转换成的类型(以ToPrimitive(x,type)为例),执行过程如下。
如果要转换成的类型type为String;
- 如果 x 是原始类型,若是直接返回
- 如果 x 是引用类型,则调用 toString(),得到原始类型就返回
- 如果得不到原始类型,则调用 valueOf(),得到原始类型就返回
- 否则报错
如果要转换成的类型type为Number;
- 如果 x 是原始类型,若是直接返回
- 如果得不到原始类型,则调用 valueOf(),得到原始类型就返回
- 如果 x 是引用类型,则调用 toString(),得到原始类型就返回
- 否则报错
前面已经聊过toString了,接下来我们聊一聊valueOf(),valueOf() 是一个所有对象都继承自 Object.prototype 的方法,valueOf() 方法返回指定对象的原始值,如下。
var boolObj = new Boolean(true);
console.log(boolObj.valueOf()); // 输出: true
var numObj = new Number(10);
console.log(numObj.valueOf()); // 输出: 10
var strObj = new String("hello");
console.log(strObj.valueOf()); // 输出: "hello"
var dateObj = new Date();
console.log(dateObj.valueOf()); // 输出: 当前日期的毫秒表示
var arrObj = [1, 2, 3];
console.log(arrObj.valueOf()); // 输出: [1, 2, 3] (数组本身)
var funcObj = function () { return "Hello"; };
console.log(funcObj.valueOf()); // 输出: function() { return "Hello"; } (函数本身)
var obj = new Object();
console.log(obj.valueOf()); // 输出: {} (对象本身)
举个对象转数字类型的例子;
let obj = {
a: 1,
b: 2
}
console.log(Number(obj))
当执行这段代码时,调用Number(obj)方法想将 obj 对象转换为Number数字类型,于是v8就会自动调用ToPrimitive(obj,Number)这个方法,然后按照上述操作步骤,首先判断obj是否是原始类型,显然不是,因为想要转换的类型为Number所以首先调用valueOf(obj);
console.log(obj.valueOf()); //{ a: 1, b: 2 }
显然{ a: 1, b: 2 }不是一个原始类型,然后调用toString();
console.log({ a: 1, b: 2 }.toString()); // [object Object]
此时[object Object]就是一个字符串了,然后直接返回,再执行Number('[object Object]')
console.log(Number('[object Object]')); // NaN
结果输出NaN,这也就是对象转数字的全过程了,当然其它对象转类型也可以以此类推。
四. 隐式类型转换的场景
隐式转换通常出现在四则运算以及判断语句中,如下;
- 四则运算 + - * / %
- 判断语句 if while == > < >= <= !=等
4.1 一元运算符 +
一元运算符会将其操作数转换为Number类型,如下;
console.log(+'1'); //1
console.log(+[]); //0
console.log(+[1, 2, 3]); //NaN
数组转数字前面提到,就是使用ToPrimitive([],Number),显然数组不是原始类型,且想转数字,先[].valueOf()得到[]然后再[].toString()得到一个空字符串,再空字符串转NumberNumber('')结果为0;
console.log([].valueOf()); // []
console.log([].toString()); // ''
console.log(Number('')); //0
4.2 二元运算符 +
console.log('1' + 2); // 12
console.log(2 + '1'); // 21
val1 + val2
左边元素为 lprim = ToPrimitive(val1),右边元素为 rprim = ToPrimitive(val2)
- 如果 lprim 或者 rprim 是字符串,另一个值直接被 ToString()
- 否则,返回对 ToNumber(lprim) 和 ToNumber(rprim) 应用加法运算的结果
ToString()(或ToNumber()):这个操作用于将一个值转换为一个数值。它不关心输入值是否是对象,而是直接将输入值(无论是原始值还是对象)转换为一个字符串(数值)。
- 如果
x为原始值,则直接将它转换成想转换的类型,ToString(x)则转换为String,ToNumber(x)则转换为Number。 - 如果
x是一个引用类型且要转换为原始值时(也就是它自己转换不了),则执行与前面的ToPrimitive类似的操作。
4.3 [ ]==![ ]
我们先来看看关于==的官方文档。
然后我们再来看
[]==![],首先有!会先将[]隐式转换为Boolean值,引用类型转换为布尔值都为true,然后再执行!true,变成[]==false,再看上述第七条,执行ToNumber(false)结果为0,
即[]==0,再看第九条,[]显然是一个对象,则变成ToPrimitive([],Number)。
上面ToPrimitive执行过程:
先valueOf([])得到一个空字符串,然后字符串显然是一个原始类型,直接返回。
于是再将空字符串转换成数字0,则此时原式变为
0 == 0,结果输出true。
好了,关于js中类型转换机制就介绍到这里了,如果觉得本篇文章对你有所帮助可以点点赞。