JS中的隐式类型转换
JavaScript是一门弱类型的语言,它在声明变量的时候不需要指定类型,对变量赋值也没有类型的检测,所以JavaScript是非常灵活的,但有些时候也会出现一些非常匪夷所思的问题,隐式类型转换便是其中之一。
什么是隐式类型转换?
JavaScript在运算时,如果两边数据类型不统一,将无法进行计算,这时编译器会自动将运算符两边的数据进行数据类型转换,转换成一样的数据类型再进行运算,这种无需程序员手动转换,而由编译器自动转换的方式就称为隐式转换。
隐式转换规则
- 转为number类型:
+
-
*
/
++
--
(算数运算符)>
<
>=
<=
==
!=
===
!==
(比较运算符); - 转为string类型:
+
不仅是算术运算符,还可以做为字符串连接符把数据转换成string类型; - 转为boolean类型:
!
(逻辑非运算符)
另外需要补充的一点常用的几种运算符和运算符优先级:
- 算术运算符:
+
、-
、*
、/
、++
、--
- 比较运算符:
>
、<
、>=
、<=
、==
、!=
、===
、!==
- 逻辑运算符:
&&
、||
、!
- 赋值运算符:
=
、+=
、-=
、*=
、/=
算术运算符 > 比较运算符 > 逻辑运算符 > 赋值运算符
三大包装类
Number
Number
对象主要用于:
下面列举一些常见的原始值
经过Number
转换后的情况。
js
// 需要注意的是,undefined 无法被转换成数字;而 null 会被转换为 0
console.log(Number(undefined)); // NaN
console.log(Number(null)); // 0
console.log(Number(true)); // 1
console.log(Number(false)); // 0
// NaN 和 Infinity 本身就是number类型
console.log(Number(10)); // 10
console.log(Number(NaN)); // NaN
console.log(Number(Infinity)); // Infinity
// 这里需要注意的是空串('')与空的字符串(' ')都会被转换为 0
console.log(Number('abc')); // NaN
console.log(Number('')); // 0
console.log(Number(' ')); // 0
String
通过String
来转换原始值的类型就相对简单了。
js
console.log(String(undefined)); // 'undefined'
console.log(String(null)); // 'null'
console.log(String(true)); // 'true'
console.log(String(false)); // 'false'
console.log(String(10)); // '10'
console.log(String(NaN)); // 'NaN'
console.log(String(Infinity)); // 'Infinity'
console.log(String('abc')); // 'abc'
console.log(String('')); // ''
console.log(String(' ')); // ' '
Boolean
falsy 值 (虚值) 是在 Boolean 上下文中认定为 false 的值。
在 JavaScript 中只有 7 个 falsy 值。
这意味着当 JavaScript 期望一个布尔值,并被给与下面值中的一个时,它总是会被当做 false。
- 值 - | 说明 |
---|---|
false |
false 关键字 |
0 | 数值 zero |
0n |
注意:这个不常见 |
“”、‘’、`` | 这是一个空字符串 (字符串的长度为零)。 |
null | null - 缺少值 |
undefined | undefined - 原始值 |
NaN | NaN - 非数值 |
其余的任何值在经过Boolean
转换时都会被认为是true
。
js
console.log(Boolean(undefined)); // false
console.log(Boolean(null)); // false
console.log(Boolean(true)); // true
console.log(Boolean(false)); // false
console.log(Boolean(10)); // true
console.log(Boolean(NaN)); // false
console.log(Boolean(Infinity)); // true
console.log(Boolean('abc')); // true
console.log(Boolean('')); // false
console.log(Boolean(' ')); // true
console.log(Boolean(0n)); // false
console.log(Boolean(0)); // false
引用值
通过Number
来转换引用值:
- 会先调用对象的
valueOf()
方法,如果valueOf()
返回原始值,将会返回该值经过Number
转换后的结果; - 如果
valueOf()
返回一个引用值,则会调用对象的toString()
方法,如果toString()
返回的是原始值,将会返回该值经过Number
转换后的结果; - 如果
toString()
返回的也是一个引用值,将会报TypeError
。
js
var test1 = {
toString () {
return '1';
},
valueOf () {
return 2;
}
}
console.log(Number(test1)); // 2
var test2 = {
toString () {
return '1';
},
valueOf () {
return [];
}
}
console.log(Number(test2)); // 1
var test3 = {
toString () {
return {};
},
valueOf () {
return [];
}
}
console.log(Number(test3)); // Uncaught TypeError: Cannot convert object to primitive value
var test4 = {};
console.log(Number(test4)); // NaN
console.log(Object.prototype.valueOf.call({})); // {}
console.log(Object.prototype.toString.call({})); // '[object Object]'
需要注意的是,对象的valueOf()
方法将会返回对象本身。
通过String
来转换引用值的行为与Number
一致,只是会先调用toString()
方法,在toString()
返回引用值时才会调用valueOf()
方法。
js
var test1 = {
toString () {
return '1';
},
valueOf () {
return 2;
}
}
console.log(String(test1)); // '1'
var test2 = {
toString () {
return [];
},
valueOf () {
return 2;
}
}
console.log(String(test2)); // '2'
var test3 = {
toString () {
return {};
},
valueOf () {
return [];
}
}
console.log(String(test3)); // Uncaught TypeError: Cannot convert object to primitive value
var test4 = {};
console.log(String(test4)); // '[object Object]'
==
运算
比较操作符会为两个不同类型的操作数转换类型,然后进行严格比较。当两个操作数都是对象时,JavaScript会比较其内部引用,当且仅当他们的引用指向内存中的相同对象(区域)时才相等,即他们在栈内存中的引用地址相同。
两边都对象的比较
js
console.log({} == {}); // false
console.log({} == []); // false
var obj1 = {},
obj2 = obj1;
console.log(obj1 == obj2); // true
对象与原始值且非字符串比较
在这种情况下的比较,会将==
两边的值都先经过Number
转换后再进行比较。
js
console.log({} == 0);
// 对象与数字比较
// {}.valueOf => {}
// {}.toString => '[object Object]'
// Number({}) => NaN
// NaN == 0
// 结果为 false
console.log([] == 0);
// 对象与数字比较
// [].valueOf() => []
// [].toString() => ''
// Number('') => 0
// 0 == 0
// 结果为 true
console.log({} == !{});
// == 左边为一个对象,而右边则是一个布尔值(false),所以会经过Number转换后再直接比较
// Number({}) => NaN
// Number(false) => 0
// NaN == 0
// 结果为 false
console.log([] == ![]);
// == 左边为一个对象,而右边则是一个布尔值(false),所以会经过Number转换后再直接比较
// Number([]) => 0
// Number(false) => 0
// 0 == 0
// 结果为 true
console.log({} == ![]);
// == 左边为一个对象,而右边则是一个布尔值(false),所以会经过Number转换后再直接比较
// Number({}) => NaN
// Number(false) => 0
// NaN == 0
// 结果为 false
两边都原始值的比较
js
// undefined
// 需要注意的是,undefined只等于它自身和null
console.log(undefined == undefined); // true
console.log(undefined == null); // true
console.log(undefined == false); // false
console.log(undefined == 0); // false
console.log(undefined == NaN); // false
console.log(undefined == ''); // false
console.log(undefined == ' '); // false
console.log(undefined == []); // false
console.log(undefined == {}); // false
// null
// 需要注意的是,null只等于它自身和undefined
console.log(null == undefined); // true
console.log(null == null); // true
console.log(null == false); // false
console.log(null == 0); // false
console.log(null == NaN); // false
console.log(null == ''); // false
console.log(null == ' '); // false
console.log(null == []); // false
console.log(null == {}); // false
// NaN
// 需要注意的是,在ES6之前,NaN不等于任何东西
console.log(NaN == undefined); // false
console.log(NaN == null); // false
console.log(NaN == false); // false
console.log(NaN == 0); // false
console.log(NaN == NaN); // false
console.log(NaN == ''); // false
console.log(NaN == ' '); // false
console.log(NaN == []); // false
console.log(NaN == {}); // false
// 空串('')
// 这里只能靠记了,没有找到相关的资料说明`''`在进行比较时的方式
console.log('' == undefined); // false
console.log('' == null); // false
console.log('' == false); // true
// Number('') => 0
// Number('') => false
// 0 == 0
// 结果为 true
console.log('' == 0); // true
// Number('') => 0
// 0 == 0
// 结果为 true
console.log('' == NaN); // false
console.log('' == ''); // true
console.log('' == ' '); // false 相同类型时会直接进行严格比较
console.log('' == []); // true
console.log('' == {}); // false
+
运算
+
操作符有两种操作
- 作为算术运算符进行加法运算
- 作为字符串拼接符号进行字符串拼接
需要注意的是,在+
操作符中,当且仅当两边均为数字时才会进行加法运行,只要遇到字符串就会进行字符串拼接
js
console.log(1 + 2 + '3'); // '33'
console.log(1 + '2'); // '12'
console.log(1 + null); // 1
console.log(1 + undefined); // NaN
console.log(1 + false); // 1
console.log(1 + true); // 2
console.log(1 + []); // '1' => 相当于 1 + ''
console.log(1 + {}); // '1[object Object]' => 1 + '[object Object]'
var test1 = {
valueOf () {
return 2;
},
toString () {
return 'test';
}
}
console.log(1 + test1); // 3
var test2 = {
valueOf () {
return {};
},
toString () {
return 'test';
}
}
console.log(1 + test2); // '1test'
console.log(+test2); // NaN
console.log(1 + Number(test2)); // NaN