前言
这是我梳理JavaScript基础系列文章中的一篇,主要记录JavaScript中的类型转换、相等性判断和运算符优先级,文尾准备了一些常见的类型转换面试题,看完本文的话,相信这些题对你们来说都是so easy的。日常博客记录在语雀,欢迎关注我的 语雀文档。
如果文章对你有帮助的话,欢迎点赞评论收藏加转发。有问题和疑惑的话也可以在评论区留言,我会第一时间回复大家,如果觉得我的文章哪里有知识点错误的话,也恳请能够告知,把错的东西理解成对的,无论在什么行业,都是致命的。
类型转换
相等性判断中存在对类型的隐式转换,我们需要先了解数据类型转换的基本规则,这里主要讨论基本数据类型、普通对象、和一些特殊的值或类型。
注意:全局属性 NaN 的值表示不是一个数字,使用isNaN进行判断。
ToString
转换成string可以使用构造方法String()和toString,区别在于toString对null和undefined会报异常;
console.log(String(000111)) //73
console.log(000111.toString()) //73
//Number.prototype.toString(radix) 有提供一个参数,指定进制,默认值为 10。
console.log(000111.toString(3)) //2201
console.log(String(1),typeof String(1))// 1 string
console.log(String(-100),typeof String(-100))// -100 string
console.log(String(1.23),typeof String(1.23))// 1.23 string
console.log(String(1e221),typeof String(1e221))// 1e+221 string
console.log(String(3333n),typeof String(3333n)) // 3333 string
console.log(String(Infinity),typeof String(Infinity))// Infinity string
console.log(String(NaN),typeof String(NaN))// NaN string
console.log(String(false),typeof String(false))// false string
console.log(String(true),typeof String(true))// true string
console.log(String(null),typeof String(null))// null string
console.log(String(undefined),typeof String(undefined))// undefined string
console.log(String([]),typeof String([]))// string
console.log(String([1]),typeof String([1]))// 1 string
console.log(String([1,'2',false,null,undefined,NaN,Infinity]),typeof String([1,'2',false,null,undefined,NaN,Infinity]))// 1,2,false,,,NaN,Infinity string
console.log(String({}),typeof String({})) // [object Object] string
console.log(String({a:1}),typeof String({a:1})) // [object Object] string
console.log(String(Symbol("foo")),typeof String(Symbol("foo"))) // Symbol(foo) string
小结
null、undefined、布尔值、NaN、Symbol分别转为它们各自的字符串;- 数值类型直接转换成数字类型的字符串(包括
Infinity,负数形式的字符串)指数表现化为e后添加一个+号,其他进制数值转为十进制; BigInt类型是去掉了后面的n符号的字符串;- 空数组转换后是空字符串,非空数组相当于使用join转换成字符串,数组中的
null和undefined,当做空字符串处理; - 对象转换为字符串为
"[object Object]"
ToNumber
转换成number可以使用构造方法Number或parseFloat、parseInt,后两者和隐式类型转换无关,本文不做阐述。
console.log(Number('12321'), typeof Number('12321'))// 12321 number
console.log(Number('1手机1'), typeof Number('1手机1'))// NaN number
console.log(Number('1.23手机'), typeof Number('1.23手机'))// NaN number
console.log(Number(''), typeof Number(''))// 0 number
console.log(Number(3333n), typeof Number(3333n)) // 3333 number
console.log(Number(Infinity), typeof Number(Infinity))// Infinity number
console.log(Number(NaN), typeof Number(NaN))// NaN number
console.log(Number(false), typeof Number(false))// 0 number
console.log(Number(true), typeof Number(true))// 1 number
console.log(Number(null), typeof Number(null))// 0 number
console.log(Number(undefined), typeof Number(undefined))// NaN number
console.log(Number([]), typeof Number([]))// 0 number
console.log(Number([1]), typeof Number([1]))// 1 number
console.log(Number([1.1]), typeof Number([1.1]))// 1.1 number
console.log(Number([1.11,2]), typeof Number([1.11,2]))// NaN number
console.log(Number(['1.11',2]), typeof Number(['1.11',2]))//NaN number
console.log(Number([1, '2', false, null, undefined, NaN, Infinity]), typeof Number([1, '2', false, null, undefined, NaN, Infinity]))// NaN number
console.log(Number({}), typeof Number({})) // NaN number
console.log(Number({a: 1}), typeof Number({a: 1})) // NaN number
console.log(Number(Symbol("foo")), typeof Number(Symbol("foo"))) // 无法转换 Cannot convert a Symbol value to a number
console.log(Number(333n), typeof Number(333n))// 333 number
小结
null和空字符、空数组转为0,undefined转为NaN,NaN和Infinity不变;- 纯数值字符串,转为对应的数字, 其他都转为
NaN; true和false转为1和0;- 数组对象可能需要进行
ToPrimitive转换,具体看后面; - 数组第一位为数值,转为该数值,否则转为
NaN; - 对象转为
NaN; BigInt是去掉了后面的n符号的数值,Symbol无法转换。
ToBoolean
转换成boolean可以使用构造方法Boolean;
console.log(Boolean(1),typeof Boolean(1))// true boolean
console.log(Boolean(-100),typeof Boolean(-100))// true boolean
console.log(Boolean(1.23),typeof Boolean(1.23))// true boolean
console.log(Boolean(1e221),typeof Boolean(1e221))// true boolean
console.log(Boolean(3333n),typeof Boolean(3333n)) // true boolean
console.log(Boolean(Infinity),typeof Boolean(Infinity))// true boolean
console.log(Boolean(NaN),typeof Boolean(NaN))// false boolean
console.log(Boolean(false),typeof Boolean(false))// false boolean
console.log(Boolean(true),typeof Boolean(true))// true boolean
console.log(Boolean(null),typeof Boolean(null))// false boolean
console.log(Boolean(undefined),typeof Boolean(undefined))// false boolean
console.log(Boolean([]),typeof Boolean([]))//true boolean
console.log(Boolean([1]),typeof Boolean([1]))// true boolean
console.log(Boolean([1,'2',false,null,undefined,NaN,Infinity]),typeof Boolean([1,'2',false,null,undefined,NaN,Infinity]))// true boolean
console.log(Boolean({}),typeof Boolean({})) // true boolean
console.log(Boolean({a:1}),typeof Boolean({a:1})) //true boolean
console.log(Boolean(Symbol("foo")),typeof Boolean(Symbol("foo"))) // true boolean
除了这8种,其他都是true
console.log(Boolean(false),typeof Boolean(false))// false boolean
console.log(Boolean(0),typeof Boolean(0))// false boolean
console.log(Boolean(-0),typeof Boolean(-0))// false boolean
console.log(Boolean(0n),typeof Boolean(0n))// false boolean
console.log(Boolean(NaN),typeof Boolean(NaN))// false boolean
console.log(Boolean(null),typeof Boolean(null))// false boolean
console.log(Boolean(undefined),typeof Boolean(undefined))// false boolean
//这三个归纳为空字符串
console.log(Boolean(""),typeof Boolean(""))// false boolean
console.log(Boolean(''),typeof Boolean(''))// false boolean
console.log(Boolean(``),typeof Boolean(``))// false boolean
小结
JavaScript中存在一个术语falsy ,falsy包含的8个值均为false,其它值转为布尔型都为true。
ToPrimitive
ToPrimitive在MDN的解释是,通过尝试调用对象的 valueOf()和toString() 方法,将参数转换为原始类型。
当对象类型需要转为原始类型时,它会先查找对象的
valueOf方法,如果valueOf方法返回原始类型的值,则ToPrimitive的结果就是这个值,如果valueOf不存在或者valueOf方法返回的不是原始类型的值,就会尝试调用对象的toString方法,也就是会遵循对象的ToString规则,然后使用toString的返回值作为ToPrimitive的结果。 注意:对于不同类型的对象来说,ToPrimitive的规则有所不同,比如Date对象会先调用toString,具体可以参考ECMA标准
valueOf和toString用法
let exp=new String(1) //使用String构造一个数字1
console.log(typeof a) //类型是 'object'
console.log(exp.valueOf()) // "1"
console.log(typeOf exp.valueOf()) //'string' 原始类型是"1" 是'string'类型
console.log(Number([])) // ''=> 0
console.log(Number(['10'])) //'10'=>10
const obj1 = {
valueOf() {
return 1
},
toString() {
return 2
}
}
console.log(Number(obj1)) // 1
const obj2 = {
toString() {
return 2
}
}
console.log(Number(obj2)) // 2
const obj3 = {
toString() {
return {}
}
}
//如果valueOf和toString都没有返回原始类型的值,则会抛出异常。
console.log(Number(obj3)) //TypeError: Cannot convert object to primitive value
小结
当对象类型需要被转为原始类型时会进行ToPrimitive转换的,我们来举几个例子。
String({}),空对象会先调用valueOf,但返回的是对象本身{},不是原始类型,所以会继续调用toString,得到'[object Object]',String('[object Object]'),所以转换后的结果为'[object Object]';Number([]),空数组会先调用valueOf,但返回的是数组本身[],不是原始类型,所以会继续调用toString,得到'',相当于Number(''),所以转换后的结果为0;- 再看上面
valueOf和toString的例子,obj1.valueOf()返回的是基本类型1,所以不会再去执行toString,ToPrimitive的结果为1,相当于Number(1); obj2没有valueOf方法,会去执行toString,toString返回2,ToPrimitive的结果为2,相当于Number(2);obj3的toString方法返回的不是一个原始类型,无法ToPrimitive,所以会抛出错误;- 注意
Boolean()转换,falsy之外的值全为true;
看到这里,我们已经总结到了类型转换的一些规则,下面我们来看看JavaScript中的相等性判断。
相等性判断
四种算法
- 非严格(抽象)相等比较:
== - 严格相等比较:
=== - 零值相等:
-0===0 - 同值相等:
Object.is()
非严格相等
MDN提供的一张对应表,我们需要记住他
undefined和null 比较都是true
console.log(undefined==null) //true
console.log(null==null) //true
console.log(undefined==null) //true
其他类型和undefined、null浅比较都是false
console.log(1==null) //false
console.log("null"==null) //false
console.log(false==null) //false
console.log(NaN==undefined) //false
console.log(true==undefined) //false
console.log(Infinity==undefined) //false
console.log({}==undefined) //false
console.log([]==undefined) //false
8个falsy值认定为false,其他值转成布尔值都是true;
console.log(!!false) //false
console.log(!!0) //false
console.log(!!-0) //false
console.log(!!0n) //false
console.log(!!'') //false
console.log(!!"") //false
console.log(!!null) //false
console.log(!!undefined)//false
console.log(!!NaN) //false
特殊情况,窄对象document.all等于null和undefined
console.log(null==document.all) //true
console.log(undefined==document.all) //true
Number、String、Boolean、Object、Array比较(遵循上表或下图):
- 类型相同为全等比较;
- 其它比较都是转成数值进行比较;
小结
undefined和null比较都是true;- 其他类型和
undefined、null比较都是false; - 8个
falsy值认定为false,其他值转成布尔值都是true; - 特殊情况,窄对象
document.all等于null和undefined Number、String、Boolean、Object、Array比较:- 类型相同为全等比较;
- 其它比较都是转成数值进行比较;
严格相等
严格相等存在两个特殊的判断:
+0等于-0(区分+0和-0在解决一些特定的数学问题时是必要的);NaN不等于NaN。
console.log(undefined===undefined) //true
console.log(null===null)//true
console.log(null===undefined) //false
console.log(+0===-0) //true
console.log(0===0) //true
console.log(NaN===NaN) //false
零值相等&&同值相等
严格相等中0和NaN的不同判断,务必会让人感到疑惑。JavaScript中使用Object.is做同值相等判断,主要就是用来解决严格相等中正零负零、NaN的问题。
console.log(Object.is(+0,-0)) //false
console.log(Object.is(NaN,NaN)) //true
运算符优先级
总结的一些规则:
- 类型转换是由操作符规定的;
ToPrimitive一般作用于==比如![]就不需要进行ToPrimitive转换,相当于!Boolean([]);- 后置
+被用作字符拼接,其他类型大多是转成数值类型; - 加法运算只要其中一个是字符串,那么另外一个也会转换为字符串;
- 数值加
null或者undefined,那么还是把null或者undefined进行Number()转换。
部分运算符优先级:
吐槽markdown的表格,这里只能上图片了,不想粘一大堆HTML,语雀表格看这里【建议星星】13道隐式类型转换面试题,让你一次爽到底
必做题目
八股题不想做也得做😣😣😣😣
题目1
定义一个变量a,使得a == 1 && a == 2 && a == 3的表达式结果为true。
//这个题可以利用我们文中提到的valueOf,每次读取都会给原值增加1
//let num=0
const a = {
num: 0,
valueOf: function() {
//return num += 1 这样也是的 只要保存一个变了就行
return this.num += 1
}
};
const equality = (a==1 && a==2 && a==3);
console.log(equality); // true
利用Object.defineProperty也能实现。
let val=1
Object.defineProperty(window, 'a', {
get: function() {
return val++
}
});
console.log(a == 1 && a == 2 && a == 3)
题目2
为什么表达式[] == 0结果为 true。
解答:
- 数组先进行
ToPrimitive转换,[].valueOf().toString(),值为空字符串; - 现在为
''==0空字符转成数字为0; 0和0比较为true,判断成立。
题目3
为什么表达式[] == ![]结果为 true。
解答:
- 这里考运算符优先级和隐式类型转换,这里需要注意,
!优先级比==高,右侧![]不需要进行ToPrimitive转换,直接!Boolean([])结果为false; - 现在是
[] == false,这个时候左侧就要进行ToPrimitive转换了,按照转换规则,[].valueOf().toString()为空字符串'' - 现在是
''==false,两侧都转成数字计算; - 空字符串转成数字是
0,右侧false转成数字也是0,0和0相等,判断成立。
题目4
解答:
//字符串'0',转成数值为0,false转成数值也是0 判断成立 判断成立
'0' == false // true
//数组只有一个值且是数字或字符串数字,转成字符串就是当前值,
//转成字符串为'0',字符串'0'转成数值为0,false转成数值也是0 判断成立
['0'] == false // true
//数组只有一个值且是数字或字符串数字,转成字符串就是当前值,
//转成字符串为'2',字符串'2'转成数值为2,2==2 判断成立
[2] == 2 //true
//空数组转成字符串为'',''空转为数值为0,false转成数值为0 判断成立
[] == false // true
//数组中的null和undefined转换成string时,当做空字符串处理,
//[null].valueOf().toString()为空字符串'',空字符串转数字为0 ,判断成立
[null] == 0 // true
///[null].valueOf().toString()为空字符串'',空字符串转数字为0,false转成数字也是0.
[null] == false // true
//数组中的null和undefined转换成string时,当做空字符串处理,
//[undefined].valueOf().toString()为空字符串'',空字符串转数字为0 ,false转成数字也是0,判断成立。
[undefined] == false // true
//其他类型和undefined、null浅比较都是false
undefined == false // false
null == 0 // false
null == false // false
题目5
求true + false的值。
解答:转换为数字相加,1+0=1
题目6
求"number" + 15 + 3
解答:'+' 运算符按从左到右的顺序的执行,所以优先执行 "number" + 15, 把15 转为 string类型,得到"number" 然后同理执行"number15" + 3,结果为"number153"。
题目7
求100 + true + 21.2 + null + undefined + "Tencent" + [] + null + 9 + false;
解答:这里没得运算符优先级,直接从左侧开始计算,ok,我们开始分解
100 + true,数值和布尔值相加,将布尔值转成数字,得到100+1=101;101 + 21.2,数值和数值直接相加,得到101 + 21.2=122.2;122.2+null,数值和null相加,null转为0,122.2+0=122.2;122.2+undefined,数值和undefined相加,undefined转成数字是NaN,NaN和数值相加均为NaN,122.2+NaN=NaN;NaN+ "Tencent",NaN转换成字符串是'NaN',"NaN"+ "Tencent"="NaNTencent";"NaNTencent" + [],空数组转成字符串是"","NaNTencent" + ""="NaNTencent";"NaNTencent" + null,null转成字符串是"null","NaNTencent" + "null"="NaNTencentnull";"NaNTencentnull" + 9,字符串和数字相加,加号有拼接的作用,把9转成字符串"9","NaNTencentnull" + "9"="NaNTencentnull9";"NaNTencentnull9" + false,字符串和布尔值相加,把false转成字符串"false","NaNTencentnull9" + "false"="NaNTencentnull9false";
let result = 100 + true + 21.2 + null + undefined + "Tencent" + [] + null + 9 + false;
console.log(result) //'NaNTencentnull9false'
题目8
求{} + [] + {} + [1]的值
解答:
- 左侧的
{}会当成一个空的执行块,而不是一个对象,这个块没有返回值,就相当于+[] + {} + [1]; +是一个一元操作符,优先执行,把[]转成数字是0,现在是0+{}+[1];- 对象转成字符串是
"[object Object]";0+"[object Object]"+[1]得到"0[object Object]"+[1]; [1]转成字符串是"1",结果为"0[object Object]1"。
let result = 100 + true + 21.2 + null + undefined + "Tencent" + [] + null + 9 + false;
console.log(result) //'NaNTencentnull9false'
题目9
求! + [] + [] + ![]的值
解答:
- 先整理优先级,逻辑非和一元加在一起时,从右到左执行
! (+ []) + [] + ![]; +被当做了一个一元操作符,(+ [])这块结果为0;- 现在为
!0+ [] + ![]=>!0对0进行布尔值装换,0是false,对false取反为true; true+ [] + ![]``[]转成布尔值是true,取反是false;[]转成字符串是'';true+ '' + false把三者拼接起来,得到"truefalse"。
题目10
求[1] > null
解答:比较运算符 > 执行 number类型隐式转换,[1]转成数字为1,null为0,1>0为true。
题目11
求"foo" + + "bar"
解答:转换一下为"foo" + (+"bar");
右侧优先级更高, (+"bar")触发number类型隐式转换,转换成数字是NaN,"foo" +NaN
拼接为'fooNaN'。
题目12
求0 || "0" && {}
解答:逻辑运算符进行Boolean类型转换,0转成布尔值是false,0 || "0" 右侧成立
"0" && {},字符串"0"是布尔值true,返回右侧,得到{}。
题目13
求[1,2,3] == [1,2,3]
解答:类型一样是,不就行类型转换,进行严格相等判断,数组是引用类型,两者内存地址不一样,为false。
结语
看了这么久了,辛苦了,不过我也写了很久啊,不妨点个赞再走吧。😁😁😁
也可以看看我这个系列的另外两篇文章,嘿嘿嘿
🎉🎉2022年元旦,我终于搞懂了原型和原型
🎉🎉⌈2022⌋ JavaScript超详细循环总结
引用
首发于语雀文档@is_tao
JavaScript 中的相等性判断
从一道面试题说起—js隐式转换踩坑合集
Falsy虚值
通过面试题研究JavaScript数据类型转换