首先来看一个代码示例:
let a = 1
let b = '1'
console.log(a == b) // true
console.log(a === b) // false
可以发现,第三行代码始终输出为true,第四行代码始终为false
按人为判断知道字符型和数字型不可能相等,所以其实这里藏着一个
js中数据类型转换的问题
== vs ===
==会发生隐式类型转换,所以只判断值是否相等===不会发生类型转换,所以会判断值和类型是否相等
那么接下来我们步入正题:js中的类型转换
类型转换 --转换规则参照表
类型转换分为显式类型转换和隐式类型转换
显式转换和隐式转换它们的转换的原理是一样,只看当前的代码场景进行选择
显式类型转换
- 转布尔
Boolean(x)
console.log(Boolean()); // false
console.log(Boolean(undefined)); // false
console.log(Boolean(null)); // false
console.log(Boolean(0)); // false
console.log(Boolean(-0)); // false
console.log(Boolean(-1)); // true
console.log(Boolean(NaN)); // false
console.log(Boolean('')); // false
console.log(Boolean('abc')); // true
- 转数字
Number(x)
console.log(Number('123')); // 123
console.log(Number('')); // 0
console.log(Number('hello')); // NaN
console.log(Number()); // 0
- 转字符串
String(x)
console.log(String(-1)); // -1
隐式类型转换
隐式转换是在js执行某些操作时自动发生的转换,比如在比较、运算符使用等场景下(对象转原始值通常发生隐式转换)。
隐式转换分类
- 原始类型转原始类型
- 引用类型转原始类型
- 转布尔 --- 任何引用类型转布尔都是
true - 转字符串 ---
String(obj)=>ToString(obj)=>ToPrimitive(obj, String) - 转数字 ---
Number(obj)=>ToNumber(obj)=>ToPrimitive(obj, Number)
- 转布尔 --- 任何引用类型转布尔都是
必要知识
valueOf()
- valueOf在对象的原型上,它只能将包装类的对象转为原始类型
let numObj = new Number(123);
console.log(numObj.valueOf()); // 123
let strObj = new String('hello');
console.log(strObj.valueOf()); // "hello"
toString()
- js中大部分的构造函数原型上都重写了 toString 方法,且复杂类型转原始类型必须用toString方法
- 对象:
{}.toString返回由'[object' 和 [[class]] 和 ']' 组成的字符串:[object class] - 数组:
[].toString返回由数组中每个元素以逗号拼接而成的字符串:[1,2,3]=>"1,2,3" - 其他:
xxx.toString直接返回xxx的字符串字面量
console.log({}.toString()); // "[object Object]"
let map = new Map();
console.log(map.toString()); // "[object Map]"
let set = new Set();
console.log(set.toString()); // "[object Set]"
console.log([].toString()); //''
console.log([1,2,3].toString()); //'1,2,3'
function myFunction() { return 'Hello'; }
console.log(myFunc.toString()); // "function myFunction() { return 'Hello'; }"
console.log((new Date()).toString()); // "Wed Sep 11 2024 00:14:53 GMT+0800 (中国标准时间)"
let numObj = new Number(123);
console.log(numObj.toString()); // "123"
ToPrimitive(obj,type) --ES5参考文档
-
ToPrimitive(obj, String)
- 判断
obj是否为原始类型,是则直接返回 - 否则,调用
toString(),如果得到了原始类型,则返回 - 否则,调用
valueOf(),如果得到了原始类型,则返回 - 否则,抛出
TypeError异常
- 判断
-
ToPrimitive(obj, Number)
- 判断
obj是否为原始类型,是则直接返回 - 否则,调用
valueOf(),如果得到了原始类型,则返回 - 否则,调用
toString(),如果得到了原始类型,则返回 (调换顺序是出于性能考虑) - 否则,抛出
TypeError异常
- 判断
发生隐式类型转换的场景
- 四则运算
+ - * / % - 判断语句
if while == >= <= != > <
加法运算符+ --ES5参考文档
作为一元运算符 -- 会发生隐式类型转换,转成
number作为二元运算符 -- 只要
+左右两边有一个是字符串,另一个也会转为字符串进行拼接
先来感受一下:
let a = + "123";
console.log(+"123"); // 123
console.log(typeof a); // number
可以看到,js引擎在执行这份代码时,自动将String类型的数据转为了number类型
console.log('' + {}); // [object,object]
原理如下:
在例1中,
+作为一元运算符,会将数据转换成number类型在例2中,
+作为二元运算符,左边为字符类型导致右边也被转换成字符串拼接输出。根据规则{}会转为[object,object]进行输出
更多练习:
console.log(1 + '1'); // "11"
// 1 + '1' ==> '11'
// ToString(1) + '1'
// '1' + '1'`
// '11'
console.log(1 + 2); // 3
console.log(null + 1) // 1
// null + 1 ==> 1
// ToNumber(null) + 1
// 0 + 1
// 1
console.log({} + '1') // '[object Object]1'
// ToPrimitive({} ,string) + '1'
// {}.toString() + '1'
// '[object Object]' + '1'
// '[object Object]1'
console.log([] + {}) // '[object Object]'
// ToPrimitive([],number) + ToPrimitive({},number)
// [].valueOf() + {}.valueOf()==> [] + {}
// [].toString() + {}.toString()
// '' + '[object Object]'
// '[object Object]'
!运算符
!会先将后面的数据转换为布尔类型的值,再取反
==运算
==操作符会尝试将两边的操作数转换为同一类型后再进行比较,基本都是转换为数字类型,如果==两边有出现!先将含有!的一边转换为布尔类型。
案例1:
console.log([] == ![])
运算过程:
- 对象转布尔一定为
true,则![] == false - 证明:
[] == false布尔值false转为数值为0 [] == 0ToPrimitive([])==>toString([])==>''==>00 == 0true
案例2:
let result = 100 + true + 21.2 + null + undefined + "Tencent" + [] + null + 9 + false
这是一道经典的面试题,运算过程和结果如下:
let result = 100 + true + 21.2 + null + undefined + "Tencent" + [] + null + 9 + false;
// result应该是?
1.首先100 + true
+连接符两边存在Number类型,true转number为1,进行加法运算,结果为:101
2.101 + 21.2
+连接符两边均为Number类型,进行加法运算,结果为:122.2
3.122.2 + null
+连接符两边存在Number类型,null转number为0,进行加法运算,结果为:122.2
4.122.2 + undefined
+连接符两边存在Number类型,undefined转number为NaN,NaN与任何数据类型计算都为NaN,结果为:NaN
5.NaN + "Tencent"
+连接符两边存在String类型,NaN转string为"NaN",进行字符串拼接,结果为:"NaNTencent"
6."NaNTencent" + []
+连接符两边存在String类型,[]转string为"",进行字符串拼接,结果为:"NaNTencent"
7."NaNTencent" + null
+连接符两边存在String类型,null转string为"null",进行字符串拼接,结果为:"NaNTencentnull"
8."NaNTencentnull" + 9
+连接符存在String类型,9转string为"9",进行字符串拼接,结果为:"NaNTencentnull9"
9."NaNTencentnull9" + false
+连接符存在String类型,false转string为"false",进行字符串拼接,结果为:"NaNTencentnull9false"