从一道面试题说起—js隐式转换踩坑合集
2018-10-26 14:47
120 查看
前方提醒: 篇幅较长,点个赞或者收藏一下,可以在下一次阅读时方便查找
提到js的隐式转换,很多人第一反应都是:坑。
的确,对于不熟悉的人来说,
js隐式转换存在着很多的让人无法预测的地方,相信很多人都深受其害,所以,大家在开发过程中,可能会使用
===来尽量避免隐式转换。但是,为了更加深入的理解
javascript,本着对知识渴望的精神,我们来通过大量的例子分析分析
js隐式转换,熟悉
js隐式转换的规则,让其在你的眼里变成“显式”。
从一道面试题说起
先来看看一个经典的面试题
定义一个变量
a,使得下面的表达式结果为true
a == 1 && a == 2 && a == 3
还有这种操作?先试试看吧,定义
a = true?
var a = true a == 1 && a == 2 && a == 3 // false
但是并没有达到预期,好像触碰到知识盲区了。。。没关系,先放下吧,来看看几个更坑的
[] == ![] // true [] == 0 // true [2] == 2 // true ['0'] == false // true '0' == false // true [] == false // true [null] == 0 // true null == 0 // false [null] == false // true null == false // false [undefined] == false // true undefined == false // false
一脸懵逼? 不要紧!接下来带你完完全全的认识
javascript的隐式转换。
javascript隐式转换规则
1. ToString,ToNumber,ToBoolean,ToPrimitive
我们需要先了解一下js数据类型之间转换的基本规则,比如数字、字符串、布尔型、数组、对象之间的相互转换。
1.1 ToString
这里所说的
ToString可不是对象的toString方法,而是指其他类型的值转换为字符串类型的操作。
这里我们讨论
null、
undefined、
布尔型、
数字、
数组、
普通对象转换为字符串的规则。
- null:转为
"null"
- undefined:转为
"undefined"
- 布尔类型:
true
和false
分别被转为"true"
和"false"
- 数字类型:转为数字的字符串形式,如
10
转为"10"
,1e21
转为"1e+21"
- 数组:转为字符串是将所有元素按照","连接起来,相当于调用数组的
Array.prototype.join()
方法,如[1, 2, 3]
转为"1,2,3"
,空数组[]
转为空字符串,数组中的null
或undefined
,会被当做空字符串处理 - 普通对象:转为字符串相当于直接使用
Object.prototype.toString()
,返回"[object Object]"
String(null) // 'null' String(undefined) // 'undefined' String(true) // 'true' String(10) // '10' String(1e21) // '1e+21' String([1,2,3]) // '1,2,3' String([]) // '' String([null]) // '' String([1, undefined, 3]) // '1,,3' String({}) // '[object Objecr]'
对象的
toString方法,满足
ToString操作的规则。
注意:上面所说的规则是在默认的情况下,如果修改默认的
toString()方法,会导致不同的结果
1.2 ToNumber
ToNumber指其他类型转换为数字类型的操作。
- null: 转为
0
- undefined:转为
NaN
- 字符串:如果是纯数字形式,则转为对应的数字,空字符转为
0
, 否则一律按转换失败处理,转为NaN
- 布尔型:
true
和false
被转为1
和0
- 数组:数组首先会被转为原始类型,也就是
ToPrimitive
,然后在根据转换后的原始类型按照上面的规则处理,关于ToPrimitive
,会在下文中讲到 - 对象:同数组的处理
Number(null) // 0 Number(undefined) // NaN Number('10') // 10 Number('10a') // NaN Number('') // 0 Number(true) // 1 Number(false) // 0 Number([]) // 0 Number(['1']) // 1 Number({}) // NaN
1.3 ToBoolean
ToBoolean指其他类型转换为布尔类型的操作。
js中的假值只有
false、
null、
undefined、
空字符、
0和
NaN,其它值转为布尔型都为
true。
Boolean(null) // false Boolean(undefined) // false Boolean('') // flase Boolean(NaN) // flase Boolean(0) // flase Boolean([]) // true Boolean({}) // true Boolean(Infinity) // true
1.4 ToPrimitive
ToPrimitive指对象类型类型(如:对象、数组)转换为原始类型的操作。
- 当对象类型需要被转为原始类型时,它会先查找对象的
valueOf
方法,如果valueOf
方法返回原始类型的值,则ToPrimitive
的结果就是这个值 - 如果
valueOf
不存在或者valueOf
方法返回的不是原始类型的值,就会尝试调用对象的toString
方法,也就是会遵循对象的ToString
规则,然后使用toString
的返回值作为ToPrimitive
的结果。
注意:对于不同类型的对象来说,
ToPrimitive的规则有所不同,比如Date对象会先调用toString,具体可以参考ECMA标准
如果
valueOf和
toString都没有返回原始类型的值,则会抛出异常。
Number([]) // 0 Number(['10']) //10 const obj1 = { valueOf () { return 100 }, toString () { return 101 } } Number(obj1) // 100 const obj2 = { toString () { return 102 } } Number(obj2) // 102 const obj3 = { toString () { return {} } } Number(obj3) // TypeError
前面说过,对象类型在
ToNumber时会先
ToPrimitive,再根据转换后的原始类型
ToNumber
Number([])
, 空数组会先调用valueOf
,但返回的是数组本身,不是原始类型,所以会继续调用toString
,得到空字符串
,相当于Number('')
,所以转换后的结果为"0"
- 同理,
Number(['10'])
相当于Number('10')
,得到结果10
obj1
的valueOf
方法返回原始类型100
,所以ToPrimitive
的结果为100
obj2
没有valueOf
,但存在toString
,并且返回一个原始类型,所以Number(obj2)
结果为102
obj3
的toString
方法返回的不是一个原始类型,无法ToPrimitive
,所以会抛出错误
看到这里,以为自己完全掌握了?别忘了,那道面试题和那一堆让人懵逼的判断还没解决呢,本着对知识渴望的精神,继续往下看吧。
2. 宽松相等(==)比较时的隐式转换规则
宽松相等(==)和
严格相等(===)的区别在于宽松相等会在比较中进行
隐式转换。现在我们来看看不同情况下的转换规则。
2.1 布尔类型和其他类型的相等比较
- 只要
布尔类型
参与比较,该布尔类型
的值首先会被转换为数字类型
- 根据
布尔类型
的ToNumber
规则,true
转为1
,false
转为0
false == 0 // true true == 1 // true true == 2 // false
之前有的人可能觉得数字
2是一个真值,所以true == 2应该为真,现在明白了,布尔类型true参与相等比较会先转为数字1,相当于1 == 2,结果当然是false
我们平时在使用
if判断时,一般都是这样写
const x = 10 if (x) { console.log(x) }
这里
if(x)的
x会在这里被转换为布尔类型,所以代码可以正常执行。但是如果写成这样:
const x = 10 if (x == true) { console.log(x) }
代码不会按照预期执行,因为
x == true相当于
10 == 1
2.2 数字类型和字符串类型的相等比较
- 当
数字类型
和字符串类型
做相等比较时,字符串类型
会被转换为数字类型
- 根据字符串的
ToNumber
规则,如果是纯数字形式的字符串,则转为对应的数字,空字符转为0
, 否则一律按转换失败处理,转为NaN
0 == '' // true 1 == '1' // true 1e21 == '1e21' // true Infinity == 'Infinity' // true true == '1' // true false == '0' // true false == '' // true
上面比较的结果和你预期的一致吗? 根据规则,字符串转为数字,布尔型也转为数字,所以结果就显而易见了。
这里就不讨论
NaN了,因为NaN和任何值都不相等,包括它自己。
2.3 对象类型和原始类型的相等比较
- 当
对象类型
和原始类型
做相等比较时,对象类型
会依照ToPrimitive
规则转换为原始类型
'[object Object]' == {} // true '1,2,3' == [1, 2, 3] // true
看一下文章开始时给出的例子
[2] == 2 // true
数组
[2]是对象类型,所以会进行
ToPrimitive操作,也就是先调用
valueOf再调用
toString,根据数组
ToString操作规则,会得到结果
"2",
而字符串
"2"再和数字
2比较时,会先转为数字类型,所以最后得到的结果为
true。
[null] == 0 // true [undefined] == 0 // true [] == 0 // true
根据上文中提到的数组
ToString操作规则,数组元素为
null或
undefined时,该元素被当做
空字符串处理,而空数组
[]也被转为
空字符串,所以上述代码相当于
'' == 0 // true '' == 0 // true '' == 0 // true
空字符串会转换为数字
0,所以结果为
true。
试试valueOf方法
const a = { valueOf () { return 10 } toString () { return 20 } } a == 10 // true
对象的
ToPrimitive操作会先调用valueOf方法,并且a的valueOf方法返回一个原始类型的值,所以ToPrimitive的操作结果就是valueOf方法的返回值10。
讲到这里,你是不是想到了最开始的面试题?
对象每次和原始类型做
==比较时,都会进行一次
ToPrimitive操作,那我们是不是可以定义一个包含
valueOf方法的对象,然后通过某个值的累加来实现?
试一试
const a = { // 定义一个属性来做累加 i: 1, valueOf () { return this.i++ } } a == 1 && a == 2 && a == 3 // true
结果正如你所想的,是正确的。当然,当没有定义
valueOf方法时,用
toString方法也是可以的。
const a = { // 定义一个属性来做累加 i: 1, toString () { return this.i++ } } a == 1 && a == 2 && a == 3 // true
2.4 null、undefined和其他类型的比较
null
和undefined
宽松相等的结果为true,这一点大家都知道
其次,
null和
undefined都是假值,那么
null == false // false undefined == false // false
居然跟我想的不一样?为什么呢? 首先,
false转为
0,然后呢? 没有然后了,
ECMAScript规范中规定
null和
undefined之间互相
宽松相等(==),并且也与其自身相等,但和其他所有的值都不
宽松相等(==)。
最后
现在再看前面的这一段代码就明了了许多
[] == ![] // true [] == 0 // true [2] == 2 // true ['0'] == false // true '0' == false // true [] == false // true [null] == 0 // true null == 0 // false [null] == false // true null == false // false [undefined] == false // true undefined == false // false
最后想告诉大家,不要一味的排斥javascript的隐式转换,应该学会如何去利用它,你的代码中可能存在着很多的隐式转换,只是你忽略了它,要做到知其然,并知其所以然,这样才能有助于我们深入的理解javascript。
(看了这么久了,辛苦了,不过我也写了很久啊,点个赞再走吧)
相关文章推荐
- 从一道面试题看C++隐式类型转换
- 从一道面试题看C++隐式类型转换
- js面试题 隐式转换、原型链分析
- 从一道面试题看C++隐式类型转换
- js引用类型和基本类型、隐式类型转换以及强制类型转换面试题
- JS面试题大坑之隐式类型转换实例代码
- js 隐式转换 一小坑
- js隐式转换:递增和递减操作符
- js中的隐式转换
- 一道面试题引发的对 JS 类型转换的思考
- 简单说 通过JS的隐式转换,关键时刻救你一命
- 聊一聊 JS 中的『隐式类型转换』
- js中数据的隐式转换
- Android多线程研究(4)——从一道面试题说起
- 简单说 通过JS的隐式转换,关键时刻救你一命
- js哪些情况会出现隐式类型转换
- js的双等号隐式转换规则
- 转:web前端面试题合集 (Javascript相关)(js异步加载详解)
- 庖丁解牛学前端1——从一个面试题说起 全局变量/闭包/值传递/IIFE
- js隐式转换的知识实例讲解