显示/隐式类型转换
显示类型转换
💄Number() 把值转为数字
- 如果参数无法被转为数字,则返回 NaN
- ‘’、’ ‘, ‘0’、false、null、[] 、 等返回 0
- {}、undefined 被转为 NaN
- 数字打头,但包含其他字符的,也返回 NaN
- 特点:
- 相比 parseInt 更宽松,false,true能转
- 但类似 ‘1a’ 这种,会 NaN
1 2 3 4 5
| var a = '123'; console.log(typeof(Number(a)) + '-' + Number(a));
a = '3.14'; console.log(typeof(Number(a)) + '-' + Number(a));
|
1 2 3 4 5 6 7 8 9 10 11
| a = 'true'; console.log(typeof(Number(a)) + '-' + Number(a));
a = 'a'; console.log(typeof(Number(a)) + '-' + Number(a));
a = '1a'; console.log(typeof(Number(a)) + '-' + Number(a));
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Number("0"); Number(""); Number(" "); Number(null); Number(false); Number([]); Number("\n"); Number("\t");
Number(true); Number('true');
Number(undefined); Number({}); Number("x");
|
💄parseInt 把值转为一个整数
- string 要被解析的值。如果参数不是一个字符串,则将其转换为字符串(使用
[ToString](https://www.ecma-international.org/ecma-262/6.0/#sec-tostring)
抽象操作)。字符串开头的空白符将会被忽略。
- radix 从
2
到 36
,表示字符串的基数。例如指定 16 表示被解析值是十六进制数。
- 当radix不填写时默认当做十进制处理,但注意10并不是默认值,当字符串以0x开头是会直接处理为16进制
parseInt("123", 16) === parseInt("0x123")
- true、false、’’、null、undefined、NaN、[]、{} 都转为 NaN
- 如果数字和字符串混合,字符串在前,转为NaN;数字在前,取数字,直到不是数字为止
- 特点:
- 相比 Number,更严格,false, true都会 NaN
- 但 ‘1a’ 这种却能处理,变成 1(也就是解析到不能解析为止)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| var a = '123'; console.log(typeof(parseInt(a)) + '-' + parseInt(a));
a = true; console.log(typeof(parseInt(a)) + '-' + parseInt(a));
a = null; console.log(typeof(parseInt(a)) + '-' + parseInt(a));
a = undefined; console.log(typeof(parseInt(a)) + '-' + parseInt(a));
a = NaN; console.log(typeof(parseInt(a)) + '-' + parseInt(a));
a = '3.14' console.log(typeof(parseInt(a)) + '-' + parseInt(a));
a = 'abc123'; console.log(typeof(parseInt(a)) + '-' + parseInt(a));
a = '123abc'; console.log(typeof(parseInt(a)) + '-' + parseInt(a));
a = '123.22abc'; console.log(typeof(parseInt(a)) + '-' + parseInt(a));
|
1 2 3 4 5 6 7 8 9 10
| var a = '10'; console.log(parseInt(a, 16));
var a = 'b'; console.log(parseInt(a, 16));
|
1 2 3 4 5 6 7 8
| parseInt(123) 123 parseInt("123") 123 parseInt("12a") 12 parseInt("a12a") NaN
|
1 2 3 4 5 6 7 8 9 10 11 12
| parseInt(12,0) 12 parseInt(12,5) 7 parseInt(12,8) 10 parseInt(112,2) 3 parseInt(112,1) NaN parseInt(123,3) 5
|
parseFloat 把值转为浮点数
1 2 3 4 5 6 7 8
| var a = '3.1415926'; console.log(typeof(parseFloat(a)) + '-' + parseFloat(a));
a = '3'; console.log(typeof(parseFloat(a)) + '-' + parseFloat(a));
a = '3.14aab'; console.log(typeof(parseFloat(a)) + '-' + parseFloat(a));
|
1 2 3 4 5
| var num = parseFloat('3.1415926'); console.log(num.toFixed(2))
console.log(3.14621.toFixed(2))
|
💄Boolean 转布尔值
- false、’’、0、undefined、null、NaN
isNaN 是否为NaN
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| console.log(isNaN(NaN)); console.log(isNaN(123));
console.log(isNaN('a'));
console.log(isNaN('NaN'));
console.log(Number.isNaN("NaN"));
console.log(isNaN(null));
console.log(isNaN(undefined));
|
隐式类型转换
前置知识
💄JS 8种内置类型
- Number
- String
- Boolean
- Undefined
- null
- Object(函数、数组都是Object的子类型)
- Symbol (ES2015)
- BigInt(ESNext stage 4)
类型分为 基本类型 和 复合类型 ,除了对象,其他都是基本类型。
ToPrimitive - 将任意值转为原始值
转换规则:
- 如果是基本类型,则不处理
- 调用
valueOf()
,并确保返回值是基本类型
- 如果没有
valueOf
这个方法或者 valueOf
返回的类型不是基本类型,那么对象会继续调用 toString()
方法
- 如果同时没有
valueOf
和 toString()
方法,或者返回的都不是基本类型,那么直接抛出 TypeError
异常
注意: 如果 preferedType=string,那么 2,3 顺序调换
转换实现:
对象 |
valueOf() |
toString() |
默认 preferedType |
Object |
原值 |
“[object Object]” |
Number |
Function |
原值 |
“function xyz() {…}” |
Number |
Array |
原值 |
“x,y,z” |
Number |
Date |
数字( |
|
|
存储的时间是从 1970 年 1 月 1 日午夜开始计的毫秒数 |
|
|
|
)e.g. |
|
|
|
1628864272562 |
“Sat May 22 2021…” |
String |
|
- 数组的
toString()
可以等效为 join(",")
,遇到 null、undefined 都被忽略,遇到 symbol 直接报错,遇到无法 ToPrimitive 的对象也报错
- 使用
模板字符串
或者使用 String(...)
包装时,preferedType=string,即优先调用 .toString()
- 使用
减法
或者 Number(...)
包装时,preferedType=number,即优先调用 .valueOf()
1 2 3 4 5
| [1, null, undefined, 2].toString();
[1, Symbol('x')].toString();
[1, Object.create(null)].toString();
|
加减法 +、-
加减法运算中遵循了一些隐式转换规则:
遇到对象先执行ToPrimitive转换为基本类型
- 加法(+)运算,preferedType是默认值
- 减法(-)运算,preferedType是Number
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 1 + {};
1 + [2, 3];
[1] + [2, 3];
function test() {}; 10 + test;
10 - undefined;
|
字符串 + 任意值(基本类型),会被处理为字符串的拼接
这里的任意值都是指基本类型,因为对象会先执行ToPrimitive变成基础类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 1 + "1";
1 + 1;
"1" + 1;
"1" + "1";
1 + "1" + 1;
"1" + false;
"1" + null;
"X" + undefined + false;
"X" + {};
|
非字符串(基本类型) + 非字符串(基本类型),两边都会先 ToNumber
这里的非字符串都是指基本类型,因为对象会先执行ToPrimitive变成基础类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| 1 + true;
1 + false;
1 + null;
1 + null + false + 1;
1 + undefined;
1 + undefined + false;
🍒 1 + undefined + [1];
🍒 1 + undefined + "1";
null + null;
1 + ![];
1 + !{};
!{} + !{};
|
任意值 - 任意值,一律执行 ToNumber,进行数字运算
此时的 preferedType === number
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 3 - 1;
3 - '1';
'3' - 1;
'3' - '1' - '2';
3 - [];
3 - {};
var date = new Date(); date.toString(); date.valueOf();
date + 1; date - 1;
|
+ x
和 一元运算 +x
是等效的(以及 -x
),都会强制 ToNumber
- +x,x在后面才Number,x在前面则不会强转Number
- a++,会当数字算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| + 0; - 0;
+ new Date();
1 + + "1";
"1" + + "1";
1 + + + + ["1"];
1 + - + - [1];
1 - + - + 1;
1 - + - + - 1;
💄1 + + [""];
1 + + ["1", "2"];
|
1 2 3 4 5 6 7 8
| var a = '1'; a++; console.log(a);
var a = '1'; a += 1; console.log(a);
|
💄经典问题 [] + {}
{} 在最前面时可能不再是对象
1 2 3
| {} + [];
{ a: 2 } + [];
|
第一个,这时 {} 其实代表的是代码块,最后就变成了 +[],根据前面的原则,数组先被转换成字符串””,接着因为+x的运算,字符串被转成数字0。
第二个,此时a不是代表对象属性,而是被当成了标签(label),标签这东西IE6就已经有了。所以如果我们写成对象是会报错的,逗号要改成分号才能通过编译。
1 2 3 4 5
| { a: 2, b: 3 } + []
{ a: 2; b: 3 } + [] === 0;
|
⚠️注意:在 Node >= 13.10.0 的版本,{}
被优先解释为空对象,仅在非对象结构的情况才会被认为是代码块。
1 2 3 4 5 6 7 8 9 10 11 12 13
|
{} + []; { a: 2 } + []; { a: 2, b: 3 } + [];
{ a: 2; b: 3 } + [];
{ var a = 1; } + []; { ; } + []; { 123 } + []; { 1 + 2 } + [];
|
定论还是下的太早了,我们还是有办法让引擎优先处理成代码块:
1 2 3 4 5 6 7
|
;{} + [] === 0; ;{ a: 2 } + [] === 0;
;{ a: 2, b: 3 } + [];
|
symbol不能加减
如果在表达式中有symbol类型,那么就会直接报错。比如1 + Symbol("x")
报错如下:
1
| Uncaught TypeError: Cannot convert a Symbol value to a number
|
💄宽松相等
相对于全等都需要对类型进行判断,当类型不一致时,宽松相等会触发隐式转换。下面介绍规则:
对象 == 对象,类型一致则不做转换
1 2 3 4 5
| {} != {}
[] != {}
[] != []
|
对象 == 基本值,对象先执行ToPrimitive转换为基本类型
1 2 3 4 5
| "[object Object]" == {}; [] == ""; [1] == "1"; [1,2] == "1,2";
|
💄布尔值 == 非布尔值,布尔值先转换成数字,再按数字规则操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| [] == false;
[1] == true;
[1,2] != true;
"0" == false;
"" == false;
|
💄数字 == 字符串,字符串ToNumber转换成数字
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| "2" == 2;
"" == 0;
[] == 0;
[1] == 1;
[1,2] != 1;
|
💄null、undefined、symbol
null、undefined与任何非自身的值对比结果都是false,但是 null == undefined
是一个特例。
1 2 3 4 5 6 7 8 9 10 11
| null == null; undefined == undefined; null == undefined;
null != 0; null != false;
undefined != 0; undefined != false;
Symbol('x') != Symbol('x');
|
比较 < >
对比不像相等,可以严格相等(===)防止类型转换,对比一定会存在隐式类型转换。
对象总是先执行ToPrimitive为基本类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| [] < []; [] == []; [] <= []; [] >= [];
[] <= {}; [] >= {}; [] == {}; [] > {}; [] < {};
{} < {}; {} == {}; {} > {}; {} >= {}; {} <= {};
|
💄两边都是字符串时才比较UNICode,否则一律转换成数字做对比
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| ["06"] < 2
["06"] < "2" ["06"] > 2
5 > null -1 < null 0 <= null
0 <= false 0 < false
5 > undefined
|
两边都是字符串,或两边都最终转为了字符串
- 两边都是字符串,则比较字符串对应的 UNICode编码
- charCodeAt(): 可以拿到字符的编码
- 可以传入一个参数,来获取字符串的第n位的编码
- 多个字符串的比较,从左到右依次比较
- 对应字符不相等,直接比较出结果
- 对应字符相等,比较下一位
-
💄总结
对象都需要先ToPrimitive转成基本类型,除非是宽松相等(==)时两个对象做对比。
-
- 全转数字,preferedType === Number
- == 同类型不转,数字优先,布尔全转数字,null、undefined、symbol不转
- < > 除非两边都是字符串,否则全部转为数字再比较
- ++/–自增自减,算术运算符(- * / %)中,字符串转number
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| 123 + '123'; 123 + null; 123 + true; 1 + NaN;
'你的岁数是:'+10+10; '你的岁数是'+(10+10); 10+10+'是你的岁数';
123 + {};
var a = '123'; a++; console.log(a);
var a = 'a'; a++; console.log(a);
var a = '3' * 2; console.log(a);
var a = 'b' * 2; console.log(a);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| console.log('1' > 2); console.log(1 > '2');
console.log('a' > 'b');
console.log(1 == '1') console.log(1 != '2')
console.log(1 === '1');
console.log(NaN == NaN);
console.log(2 > 1 > 3);
console.log(2 > 1 == 1);
var num = '123'; console.log(typeof(- num) + ':' + - num);
console.log(typeof(+ num) + ':' + + num);
num = 'abc'; console.log(typeof(+ num) + ':' + + num);
|
资源
关于JS隐式类型转换的完整总结