面试题
JavaScript中如何进行隐式类型转换?以及什么情况下会进行隐式类型转换?
其他值到字符串的转换规则?
其他值到数字值的转换规则?
其他值到布尔类型值的转换规则?
(a == 1) && (a == 2) && (a == 3)能不能为 true?
什么是类型转换
类型转换(Type conversion/typecasting)是指将数据由一种类型变换为另一种类型。JavaScript 作为一种弱类型的语言,不用像 C 语言那样要定义好数据类型,因为允许变量类型的 隐式类型转换 和 显式类型转换,其中比较常见的类型转换有三种:
- 转为字符串类型
- 转为数字类型
- 转为布尔类型
显式类型转换
显式类型转换就是手动地将一种值转换为另一种值。常用的显式类型转换方法有 Number
、String
、Boolean
、parseInt
、parseFloat
、toString
等等。
-
转换为数值类型
- Number(string)
- parsetInt(string, radix)
- parseFloat(string)
-
转换为字符串类型
- String(string)
- toString(string)
-
转换为布尔类型
- Boolean(string)
下面列出常见数据类型分别显式转换为数值类型、字符串类型以及布尔类型的值
表 1.1 显式类型转换
数据类型 | 数据值 | 转数值值(Number方法) | 转字符串值(String方法) | 转布尔值(Boolean方法) |
---|---|---|---|---|
Undefined | undefined | NaN | 'undefined' | false |
NULL | null | 0 | 'null' | false |
String | ‘12’、‘12ss’ | 12、NaN | ‘12’、‘12ss’ | true、true |
Number | 1 | 1 | '1' | true |
Boolean | true、false | 1、0 | 'true'、‘fasle’ | true、false |
Symbol | Symbol() | TypeError | ‘Symbol()’ | true |
BigInt | 9999999999n | 999999999 | ‘99999999’ | true |
Object | {}、{name: '22'} | NaN、NaN | ‘[object Object]’、‘[object Object]’ | true、true |
Array | []、[1]、[1,2,3] | 0、1、NaN | ‘’、‘1’、‘1,2,3’ | true、true、true |
下面分别介绍用于显式转换的常见函数。 |
Number()
-
原始类型值
-
undefined:转为 NaN
-
null:转为 NaN
-
string:如果可以被解析为数值,则转为相应的数值,否则转为 NaN
-
number:转为原来的数值
-
Boolean:true 转为 1,false转为 0
-
Symbol:不可以转换,返回 TypeError
-
BigInt:转为对应的数字,如果超过最大安全数,会存在精度丢失问题
// 超出最大安全数存在丢失精度问题 Number(9107199254740991n) // 9107199254740992
-
-
引用类型
- 首先调用自身的 vauleOf 方法,如果返回原始类型的值,则直接对该值使用 Number 函数,否则改为调用对象自身的 toString 方法
- 如果 toString 方法返回原始类型的值,则对该值使用 Number 函数
- 如果 toString 方法返回的是对象,就报错
String()
-
原始类型值
- undefined:转为 'undefined'
- null:转为 'null'
- string:转为原来的数值
- number:转为相应的字符串
- Boolean:true 转为 ‘true',false转为 ’false‘
- Symbol:转为相应的字符串
- BigInt:转为相应的字符串
-
引用类型
- 首先调用自身的 toString方法,如果返回原始类型的值,则直接对该值使用 String函数,否则改为调用对象自身的 valueOf 方法
- 如果 valueOf 方法返回原始类型的值,则对该值使用 String 函数
- 如果 valueOf 方法返回的是对象,就报错
Boolean()
-
返回为false的情况
- undefined
- null
- ’’
- 0
- 0n
- NaN
-
其他返回全为true
隐式类型转换
隐式类型转换就是无需开发者手动转换,而由 编译器自动转换 的方式就称为 隐式类型转换。在 JavaScript 中,当运算符在运算时,如果 两边数据不统一,CPU 就无法运算,这时我们编译器会自动将运算符两边的数据做一个数据类型转换,转成相同的数据类型再计算,常见的自动转换场景有如下三种情况:
- 第一种情况,不同类型的数据互相运算。
1 + '1'
> 11
- 第二种情况,对非布尔值类型的数据求布尔值。
if (1) {
console.log(true)
}
> true
- 第三种情况,对非数值类型的值使用一元运算符(即
+
和-
)。
+ '9'
> 9
下面具体讲解分别转换为数值类型、字符串类型以及布尔类型的常见情况:
转换为数值类型
JavaScript 遇到预期为数值的地方,就会将参数值自动转换为数值。如果是基本数据类型,则直接调用调用Number()
函数,如果是引用数据类型,则是先转化为基本数据类型,再调用Number()
函数。
- 除了加法运算符(
+
)有可能把运算子转为字符串,其他运算符(算数运算符、等于运算符、比较运算符、一元运算符(!除外))都会把运算子自动转成数值。
'3' - 1
> 2
'3' * 2
> 6
'6' / 3
> 2
'1' == true
> true
'1' != true
> false
'2' > 1
> true
! true
+ '1'
> 1
- 当加号运算符作为二元运算符操作值时,如果两个操作数都不是 String 类型,两个操作数会调用
Number()
方法隐式转换为 Number 类型,然后进行加法算术运算
1 + 1
> 2
1 + true
> 2
1 + NaN
> NaN
转换为字符串类型
JavaScript 遇到预期为字符串的地方,就会将参数值自动转换为字符串。如果是基本数据类型,则直接调用调用String()
函数,如果是引用数据类型,则是先转化为基本数据类型,再调用String()
函数。转换为字符串类型比较常见的场景有:
- 当加号运算符作为二元运算符操作值时,如果两个操作数中只要存在一个操作数为 String 类型,那么另一个操作数会调用
String()
方法转成字符串然后进行拼接
1 + '1'
> '11'
1 + {}
> '1[object Object]'
- 很多内置函数期望传入的参数为 String 类型,但是如果我们传入的是 Number 类型或者 Object 类型等非 String 类型的数据的时候,就会发生数据类型的隐式转换
alert
parsetInt
转换为布尔类型
JavaScript 遇到预期为布尔的地方,就会将参数值自动转换为布尔。系统内部会自动调用Boolean()
函数。转换为布尔类型比较常见的场景有:
- 逻辑运算(!)
! '2'
> false
- 逻辑判断( if 语句)
if('1') {
console.log(true)
}
> true
下面列出常见数据类型分别隐式转换为数值类型、字符串类型以及布尔类型的值
表 1.2 隐式类型转换
数据类型 | 数据值 | 转数值值(+) | 转字符串值(+) | 转布尔值(!) |
---|---|---|---|---|
Undefined | undefined | NaN | 'undefined' | false |
NULL | null | 0 | 'null' | false |
String | ‘12’、‘12ss’ | 12、NaN | ‘12’、‘12ss’ | true、true |
Number | 1 | 1 | '1' | true |
Boolean | true、false | 1、0 | 'true'、‘fasle’ | true、false |
Symbol | Symbol() | TypeError | TypeError | true |
BigInt | 9999999999n | TypeError | ‘99999999’ | true |
Object | {}、{name: '22'} | NaN、NaN | ‘[object Object]’、‘[object Object]’ | true、true |
Array | []、[1]、[1,2,3] | 0、1、NaN | ‘’、‘1’、‘1,2,3’ | true、true、true |
总结
对常见数据类型通过显式类型转换和隐式类型转换将其一一转换为数值类型、字符串类型以及布尔类型的值,通过下表1.3和1.4分析可知,只有Symbol在隐式转换为字符串以及BigInt在隐式转换为数值会报TypeError之外,其他完成一致。
表 1.3 显式类型转换
数据类型 | 数据值 | 转数值值(Number方法) | 转字符串值(String方法) | 转布尔值(Boolean方法) |
---|---|---|---|---|
Undefined | undefined | NaN | 'undefined' | false |
NULL | null | 0 | 'null' | false |
String | ‘12’、‘12ss’ | 12、NaN | ‘12’、‘12ss’ | true、true |
Number | 1 | 1 | '1' | true |
Boolean | true、false | 1、0 | 'true'、‘fasle’ | true、false |
Symbol | Symbol() | TypeError | ‘Symbol()’ | true |
BigInt | 9999999999n | 999999999 | ‘99999999’ | true |
Object | {}、{name: '22'} | NaN、NaN | ‘[object Object]’、‘[object Object]’ | true、true |
Array | []、[1]、[1,2,3] | 0、1、NaN | ‘’、‘1’、‘1,2,3’ | true、true、true |
表 1.4 隐式类型转换
数据类型 | 数据值 | 转数值值(+) | 转字符串值(+) | 转布尔值(!) |
---|---|---|---|---|
Undefined | undefined | NaN | 'undefined' | false |
NULL | null | 0 | 'null' | false |
String | ‘12’、‘12ss’ | 12、NaN | ‘12’、‘12ss’ | true、true |
Number | 1 | 1 | '1' | true |
Boolean | true、false | 1、0 | 'true'、‘fasle’ | true、false |
Symbol | Symbol() | TypeError | TypeError | true |
BigInt | 9999999999n | TypeError | ‘99999999’ | true |
Object | {}、{name: '22'} | NaN、NaN | ‘[object Object]’、‘[object Object]’ | true、true |
Array | []、[1]、[1,2,3] | 0、1、NaN | ‘’、‘1’、‘1,2,3’ | true、true、true |
同时由于自动转换具有不确定性,而且不易找出错误,建议在预期为布尔值、数值、字符串的地方,全部使用Boolean()
、Number()
和String()
函数进行显式转换。
答案
问1:JavaScript中如何进行隐式类型转换?以及什么情况下会进行隐式类型转换?
答:隐式类型转换就是无需开发者手动转换,而由编译器自动转换的方式就称为隐式类型转换。对于原始数据类型调用
对应的Number、String和Boolean函数即可,对于引用数据类型,则是先转化为原始数据类型,再调用对应的
Number、String和Boolean函数。隐式转换的常见场景有:
1. 不同类型的数据互相运算
2. 对非布尔值类型的数据求布尔值
3. 对非数值类型的值使用一元运算符
引用类型转数值类型的策略:
- 首先调用自身的 vauleOf 方法,如果返回原始类型的值,则直接对该值使用 Number 函数,否则改为调用对象自身
的 toString 方法
- 如果 toString 方法返回原始类型的值,则对该值使用 Number 函数
- 如果 toString 方法返回的是对象,就报错
引用类型转字符串类型的策略:
- 首先调用自身的 toString方法,如果返回原始类型的值,则直接对该值使用 String函数,否则改为调用对象自身
的 valueOf 方法
- 如果 valueOf 方法返回原始类型的值,则对该值使用 String 函数
- 如果 valueOf 方法返回的是对象,就报错
引用类型转布尔类型一律为 true
问2:其他值到字符串的转换规则?
答:原始类型值如果是显式转换,调用对应的显式函数即可,如果是隐式调用则系统自调用 String() 函数,引用类型
值首先调用自身的 toString 方法,如果返回原始类型的值,则直接对该值使用显式函数或 String 函数,否则改为
调用对象自身的 valueOf 方法,如果 valueOf 方法返回原始类型的值,则对该值使用显式函数或 String 函数,如
果 valueOf 方法返回的是对象,就报错
据类型 | 数据值 | 显式转字符串值(String方法) | 隐式转字符串值(+) |
---|---|---|---|
Undefined | undefined | 'undefined' | 'undefined' |
NULL | null | 'null' | 'null' |
String | ‘12’、‘12ss’ | ‘12’、‘12ss’ | ‘12’、‘12ss’ |
Number | 1 | '1' | '1' |
Boolean | true、false | 'true'、‘fasle’ | 'true'、‘fasle’ |
Symbol | Symbol() | ‘Symbol()’ | TypeError |
BigInt | 9999999999n | ‘99999999’ | ‘99999999’ |
Object | {}、{name: '22'} | ‘[object Object]’、‘[object Object]’ | ‘[object Object]’、‘[object Object]’ |
Array | []、[1]、[1,2,3] | ‘’、‘1’、‘1,2,3’ | ‘’、‘1’、‘1,2,3’ |
问3:其他值到数字值的转换规则?
答:原始类型值如果是显式转换,调用对应的显式函数即可,如果是隐式调用则系统自调用 Number() 函数,引用类型
值首先调用自身的 valueOf 方法,如果返回原始类型的值,则直接对该值使用显式函数或 Number 函数,否则改为调
用对象自身的 toString 方法,如果 toString 方法返回原始类型的值,则对该值使用显式函数或 Number 函数,如
果 toString 方法返回的是对象,就报错
数据类型 | 数据值 | 转数值值(Number方法) | 转数值值(+) |
---|---|---|---|
Undefined | undefined | NaN | NaN |
NULL | null | 0 | 0 |
String | ‘12’、‘12ss’ | 12、NaN | 12、NaN |
Number | 1 | 1 | 1 |
Boolean | true、false | 1、0 | 1、0 |
Symbol | Symbol() | TypeError | TypeError |
BigInt | 9999999999n | 999999999 | TypeError |
Object | {}、{name: '22'} | NaN、NaN | NaN、NaN |
Array | []、[1]、[1,2,3] | 0、1、NaN | 0、1、NaN |
问4:其他值到布尔类型值的转换规则?
答:原始类型值如果是显式转换,调用对应的显式函数即可,如果是隐式调用则系统自调用 Boolean() 函数,引用类
型值一律返回 true
数据类型 | 数据值 | 转数值值(+) | 转布尔值(!) |
---|---|---|---|
Undefined | undefined | false | false |
NULL | null | false | false |
String | ‘12’、‘12ss’ | true、true | true、true |
Number | 1 | true | true |
Boolean | true、false | true、false | true、false |
Symbol | Symbol() | true | true |
BigInt | 9999999999n | true | true |
Object | {}、{name: '22'} | true、true | true、true |
Array | []、[1]、[1,2,3] | true、true、true | true、true、true |
问5:(a == 1) && (a == 2) && (a == 3)能不能为 true?
答:可以,引用类型值转数值类型,首先调用自身的 valueOf 方法,如果返回原始类型的值,则直接对该值使用显式
函数或 Number 函数,否则改为调用对象自身的 toString 方法,如果 toString 方法返回原始类型的值,则对该值
使用显式函数或 Number 函数,如果 toString 方法返回的是对象,就报错
const a = {
num: 0,
valueOf: function() {
return (this.num+=1)
}
}
console.log((a == 1) && (a == 2) && (a == 3))
> true
这里的 a 每次调用 valueOf 函数都会自加 1
附加题
换一种方式使得 (a == 1) && (a == 2) && (a == 3)?