前言
这是我梳理JavaScript
根底系列文章中的一篇,次要记录JavaScript
中的类型转换、相等性判断和运算符优先级,文尾筹备了一些常见的类型转换面试题,看完本文的话,置信这些题对你们来说都是so easy的。日常博客记录在语雀,欢送关注我的 语雀文档。
如果文章对你有帮忙的话,欢送点赞评论珍藏加转发。有问题和纳闷的话也能够在评论区留言,我会第一工夫回复大家,如果感觉我的文章哪里有知识点谬误的话,也恳请可能告知,把错的货色了解成对的,无论在什么行业,都是致命的。
类型转换
相等性判断中存在对类型的隐式转换,咱们须要先理解数据类型转换的根本规定,这里次要探讨根本数据类型、一般对象、和一些非凡的值或类型。
留神:全局属性 NaN
的值示意不是一个数字,应用isNaN
进行判断。
ToString
转换成string
能够应用构造方法String()
和toString
,区别在于toString
对null
和undefined
会报异样;
console.log(String(000111)) //73console.log(000111.toString()) //73//Number.prototype.toString(radix) 有提供一个参数,指定进制,默认值为 10。console.log(000111.toString(3)) //2201
console.log(String(1),typeof String(1))// 1 stringconsole.log(String(-100),typeof String(-100))// -100 stringconsole.log(String(1.23),typeof String(1.23))// 1.23 stringconsole.log(String(1e221),typeof String(1e221))// 1e+221 stringconsole.log(String(3333n),typeof String(3333n)) // 3333 stringconsole.log(String(Infinity),typeof String(Infinity))// Infinity stringconsole.log(String(NaN),typeof String(NaN))// NaN stringconsole.log(String(false),typeof String(false))// false stringconsole.log(String(true),typeof String(true))// true stringconsole.log(String(null),typeof String(null))// null stringconsole.log(String(undefined),typeof String(undefined))// undefined stringconsole.log(String([]),typeof String([]))// stringconsole.log(String([1]),typeof String([1]))// 1 stringconsole.log(String([1,'2',false,null,undefined,NaN,Infinity]),typeof String([1,'2',false,null,undefined,NaN,Infinity]))// 1,2,false,,,NaN,Infinity stringconsole.log(String({}),typeof String({})) // [object Object] stringconsole.log(String({a:1}),typeof String({a:1})) // [object Object] stringconsole.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 numberconsole.log(Number('1手机1'), typeof Number('1手机1'))// NaN numberconsole.log(Number('1.23手机'), typeof Number('1.23手机'))// NaN numberconsole.log(Number(''), typeof Number(''))// 0 numberconsole.log(Number(3333n), typeof Number(3333n)) // 3333 numberconsole.log(Number(Infinity), typeof Number(Infinity))// Infinity numberconsole.log(Number(NaN), typeof Number(NaN))// NaN numberconsole.log(Number(false), typeof Number(false))// 0 numberconsole.log(Number(true), typeof Number(true))// 1 numberconsole.log(Number(null), typeof Number(null))// 0 numberconsole.log(Number(undefined), typeof Number(undefined))// NaN numberconsole.log(Number([]), typeof Number([]))// 0 numberconsole.log(Number([1]), typeof Number([1]))// 1 numberconsole.log(Number([1.1]), typeof Number([1.1]))// 1.1 numberconsole.log(Number([1.11,2]), typeof Number([1.11,2]))// NaN numberconsole.log(Number(['1.11',2]), typeof Number(['1.11',2]))//NaN numberconsole.log(Number([1, '2', false, null, undefined, NaN, Infinity]), typeof Number([1, '2', false, null, undefined, NaN, Infinity]))// NaN numberconsole.log(Number({}), typeof Number({})) // NaN numberconsole.log(Number({a: 1}), typeof Number({a: 1})) // NaN numberconsole.log(Number(Symbol("foo")), typeof Number(Symbol("foo"))) // 无奈转换 Cannot convert a Symbol value to a numberconsole.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 booleanconsole.log(Boolean(-100),typeof Boolean(-100))// true booleanconsole.log(Boolean(1.23),typeof Boolean(1.23))// true booleanconsole.log(Boolean(1e221),typeof Boolean(1e221))// true booleanconsole.log(Boolean(3333n),typeof Boolean(3333n)) // true booleanconsole.log(Boolean(Infinity),typeof Boolean(Infinity))// true booleanconsole.log(Boolean(NaN),typeof Boolean(NaN))// false booleanconsole.log(Boolean(false),typeof Boolean(false))// false booleanconsole.log(Boolean(true),typeof Boolean(true))// true booleanconsole.log(Boolean(null),typeof Boolean(null))// false booleanconsole.log(Boolean(undefined),typeof Boolean(undefined))// false booleanconsole.log(Boolean([]),typeof Boolean([]))//true booleanconsole.log(Boolean([1]),typeof Boolean([1]))// true booleanconsole.log(Boolean([1,'2',false,null,undefined,NaN,Infinity]),typeof Boolean([1,'2',false,null,undefined,NaN,Infinity]))// true booleanconsole.log(Boolean({}),typeof Boolean({})) // true booleanconsole.log(Boolean({a:1}),typeof Boolean({a:1})) //true booleanconsole.log(Boolean(Symbol("foo")),typeof Boolean(Symbol("foo"))) // true boolean
除了这
8
种,其余都是true
console.log(Boolean(false),typeof Boolean(false))// false booleanconsole.log(Boolean(0),typeof Boolean(0))// false booleanconsole.log(Boolean(-0),typeof Boolean(-0))// false booleanconsole.log(Boolean(0n),typeof Boolean(0n))// false booleanconsole.log(Boolean(NaN),typeof Boolean(NaN))// false booleanconsole.log(Boolean(null),typeof Boolean(null))// false booleanconsole.log(Boolean(undefined),typeof Boolean(undefined))// false boolean//这三个演绎为空字符串console.log(Boolean(""),typeof Boolean(""))// false booleanconsole.log(Boolean(''),typeof Boolean(''))// false booleanconsole.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结构一个数字1console.log(typeof a) //类型是 'object'console.log(exp.valueOf()) // "1"console.log(typeOf exp.valueOf()) //'string' 原始类型是"1" 是'string'类型
console.log(Number([])) // ''=> 0console.log(Number(['10'])) //'10'=>10const obj1 = { valueOf() { return 1 }, toString() { return 2 }}console.log(Number(obj1)) // 1const obj2 = { toString() { return 2 }}console.log(Number(obj2)) // 2const 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) //trueconsole.log(null==null) //trueconsole.log(undefined==null) //true
其余类型和
undefined
、null
浅比拟都是false
console.log(1==null) //falseconsole.log("null"==null) //falseconsole.log(false==null) //falseconsole.log(NaN==undefined) //falseconsole.log(true==undefined) //falseconsole.log(Infinity==undefined) //falseconsole.log({}==undefined) //falseconsole.log([]==undefined) //false
8个
falsy
值认定为false
,其余值转成布尔值都是true
;console.log(!!false) //falseconsole.log(!!0) //falseconsole.log(!!-0) //falseconsole.log(!!0n) //falseconsole.log(!!'') //falseconsole.log(!!"") //falseconsole.log(!!null) //falseconsole.log(!!undefined)//falseconsole.log(!!NaN) //false
非凡状况,窄对象
document.all
等于null
和undefined
console.log(null==document.all) //trueconsole.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) //trueconsole.log(null===null)//trueconsole.log(null===undefined) //falseconsole.log(+0===-0) //trueconsole.log(0===0) //trueconsole.log(NaN===NaN) //false
零值相等&&同值相等
严格相等中
0
和NaN
的不同判断,务必会让人感到纳闷。JavaScript
中应用Object.is
做同值相等判断,次要就是用来解决严格相等中正零负零、NaN
的问题。console.log(Object.is(+0,-0)) //falseconsole.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=0const 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=1Object.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浅比拟都是falseundefined == false // falsenull == 0 // falsenull == 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数据类型转换