JS中的隐式类型转换

前端开发
2020年02月13日
587

JavaScript是一门弱类型的语言,它在声明变量的时候不需要指定类型,对变量赋值也没有类型的检测,所以JavaScript是非常灵活的,但有些时候也会出现一些非常匪夷所思的问题,隐式类型转换便是其中之一。

什么是隐式类型转换?

JavaScript在运算时,如果两边数据类型不统一,将无法进行计算,这时编译器会自动将运算符两边的数据进行数据类型转换,转换成一样的数据类型再进行运算,这种无需程序员手动转换,而由编译器自动转换的方式就称为隐式转换。

隐式转换规则

  1. 转为number类型:+ - * / ++ --(算数运算符) > < >= <= == != === !==(比较运算符);
  2. 转为string类型:+ 不仅是算术运算符,还可以做为字符串连接符把数据转换成string类型;
  3. 转为boolean类型: !(逻辑非运算符)

另外需要补充的一点常用的几种运算符和运算符优先级:

  • 算术运算符:+-*/++--
  • 比较运算符: ><>=<===!====!==
  • 逻辑运算符:&&||!
  • 赋值运算符:=+=-=*=/=

算术运算符 > 比较运算符 > 逻辑运算符 > 赋值运算符

三大包装类

Number

Number 对象主要用于:

  • 如果参数无法被转换为数字,则返回 NaN
  • 在非构造器上下文中 (如:没有 new 操作符),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来转换引用值:

  1. 会先调用对象的valueOf()方法,如果valueOf()返回原始值,将会返回该值经过Number转换后的结果;
  2. 如果valueOf()返回一个引用值,则会调用对象的toString()方法,如果toString()返回的是原始值,将会返回该值经过Number转换后的结果;
  3. 如果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

+运算

+操作符有两种操作

  1. 作为算术运算符进行加法运算
  2. 作为字符串拼接符号进行字符串拼接

需要注意的是,在+操作符中,当且仅当两边均为数字时才会进行加法运行,只要遇到字符串就会进行字符串拼接

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