# 类型转换
[toc]
将值从一种类型转换为另一种类型通常称为类型转换。
ES6 前,JavaScript 共有六种数据类型:Undefined、Null、Boolean、Number、String、Object。后面又新增Symbol 和BigInt。
# 1、原始值 => 布尔
我们使用Boolean函数将类型转换为布尔类型,在js中,只有6种值可以被转换成false, 其他都被转换成true。
console.log(Boolean()); // false
console.log(Boolean(false)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean(null)); // false
console.log(Boolean(+0)); // false
console.log(Boolean(-0)); // false
console.log(Boolean(NaN)); // false
console.log(Boolean('')); // false
需要注意的是,不传递任何参数时,返回的是false。
# 2、原始值 => 数字
我们可以使用 Number 函数将类型转换成数字类型,如果参数无法被转换为数字,则返回 NaN。
如果 Number 函数不传参数,返回 +0,如果有参数,调用 ToNumber(value)
。
注意这个 ToNumber
表示的是一个底层规范实现上的方法,并没有直接暴露出来。
参数类型 | 返回值 |
---|---|
Undefined | NaN |
Null | +0 |
Boolean | The result is 1 if the argument is true. The result is +0 if the argument is false. |
Number | 返回与之相等的值 |
String | 比较复杂看下面的例子。 |
Object | Apply the following steps:Let primValue be ToPrimitive (opens new window)(input argument, hint Number).Return ToNumber(primValue).调用 对象的toPrimitive,获取原始值, 再用原始值调用toNumber |
console.log(Number()) // +0
console.log(Number(undefined)) // NaN
console.log(Number(null)) // +0
console.log(Number(false)) // +0
console.log(Number(true)) // 1
console.log(Number("123")) // 123
console.log(Number("-123")) // -123
console.log(Number("1.2")) // 1.2
console.log(Number("000123")) // 123
console.log(Number("-000123")) // -123
console.log(Number("0x11")) // 17
console.log(Number("")) // 0
console.log(Number(" ")) // 0
console.log(Number("123 123")) // NaN
console.log(Number("foo")) // NaN
console.log(Number("100a")) // NaN
如果通过 Number 转换函数传入一个字符串,它会试图将其转换成一个整数或浮点数,而且会忽略所有前导的 0,如果有一个字符不是数字,结果都会返回 NaN。
# 3、原始值 => 字符串
我们使用 String
函数将类型转换成字符串类型。如果 String
函数不传参数,返回空字符串,如果有参数,调用 ToString(value)
,而 ToString
也给了一个对应的结果表。表如下:
参数类型 | 结果 |
---|---|
Undefined | "undefined" |
Null | "null" |
Boolean | 如果参数是 true,返回 "true"。参数为 false,返回 "false" |
Number | 又是比较复杂,可以看例子 |
String | 返回与之相等的值 |
console.log(String(undefined)); // 'undefined'
console.log(String(null)); // 'null'
console.log(String(false)); // 'false'
console.log(String(true)); // 'true'
console.log(String(0)); // '0'
console.log(String(-0)); // '0'
console.log(String(NaN)); // 'NaN'
console.log(String(Infinity)); // 'Infinity'
console.log(String(-Infinity)); // '-Infinity'
const str1 = String(1);
console.log(str1, typeof str1); // '1' string
console.log(str1 === '1'); // true
console.log(str1 === 1); // false
# 4、原始值 => 对象
原始值到对象的转换非常简单,原始值通过调用 String()、Number() 或者 Boolean() 构造函数,转换为它们各自的包装对象。
null 和 undefined 属于例外,当将它们用在期望是一个对象的地方都会造成一个类型错误 (TypeError) 异常,而不会执行正常的转换。
var a = 1;
console.log(typeof a); // number
var b = new Number(a);
console.log(typeof b); // object
String()、Number() 、 Boolean() 可以称为包装类。
# 5、对象 => 布尔值
对象到布尔值的转换非常简单:所有对象(包括数组和函数)都转换为 true。
# 6、对象 => 字符串和数字
# 6.1 toString()
对象转字符串和数字都是通过被转换对象的一个方法来完成的。而 JavaScript 对象有两个不同的方法来执行转换,一个是 toString
,一个是 valueOf
。所有的对象除了 null 和 undefined 之外的任何值都具有 toString
方法,通常情况下,它和使用 String 方法返回的结果一致。toString
方法的作用在于返回一个反映这个对象的字符串,然而这才是情况复杂的开始。
# 6.3 valueOf()
基础类型(Number,String.Boolean等)的valueOf返回其本身
而另一个转换对象的函数是 valueOf,表示对象的原始值。默认的 valueOf 方法返回这个对象本身,数组、函数、正则简单的继承了这个默认方法,也会返回对象本身。日期是一个例外,它会返回它的一个内容表示: 1970 年 1 月 1 日以来的毫秒数。
const arr = [1];
console.log(arr.valueOf() === arr); // true
console.log(new Date(2023, 7 - 1, 31).valueOf()); // 1690732800000
# 6.3 Object toString的步骤
参数类型 | 结果 |
---|---|
Object | 1. primValue = ToPrimitive(input, String) 2. 返回ToString(primValue). |
所谓的 ToPrimitive 方法,其实就是输入一个值,然后返回一个一定是基本类型的值。这个ToPrimitive 不是一个暴露的API。
# 6.3 Object toNumber的步骤
参数类型 | 结果 |
---|---|
Object | 1. primValue = ToPrimitive(input, String) 2. 返回toNumber(primValue). |
# 6.4 ToPrimitive
ToPrimitive(input[, PreferredType])
第一个参数是 input,表示要处理的输入值。
第二个参数是 PreferredType,非必填,表示希望转换成的类型,有两个值可以选,Number 或者 String。
当不传入 PreferredType 时,如果 input 是日期类型,相当于传入 String,否则,都相当于传入 Number。
如果传入的 input 是 Undefined、Null、Boolean、Number、String 类型,直接返回该值。
如果是 ToPrimitive(obj, Number),处理步骤如下:
- 如果 obj 为 基本类型,直接返回
- 否则,调用 valueOf 方法,如果返回一个原始值,则 JavaScript 将其返回。
- 否则,调用 toString 方法,如果返回一个原始值,则 JavaScript 将其返回。
- 否则,JavaScript 抛出一个类型错误异常。
如果是 ToPrimitive(obj, String),处理步骤如下:
- 如果 obj为 基本类型,直接返回
- 否则,调用 toString 方法,如果返回一个原始值,则 JavaScript 将其返回。
- 否则,调用 valueOf 方法,如果返回一个原始值,则 JavaScript 将其返回。
- 否则,JavaScript 抛出一个类型错误异常。
TIP
toPrimitive的默认行为可以通过复写 Symbol.toPrimitive 属性覆盖。
# 6.5 对象转字符串 总结
所以总结下,对象转字符串(就是 String() 函数)可以概括为:
- 如果对象具有 toString 方法,则调用这个方法。如果他返回一个原始值,JavaScript 将这个值转换为字符串,并返回这个字符串结果。
- 如果对象没有 toString 方法,或者这个方法并不返回一个原始值,那么 JavaScript 会调用 valueOf 方法。如果存在这个方法,则 JavaScript 调用它。如果返回值是原始值,JavaScript 将这个值转换为字符串,并返回这个字符串的结果。
- 否则,JavaScript 无法从 toString 或者 valueOf 获得一个原始值,这时它将抛出一个类型错误异常。
# 6.6 对象转数字 总结
对象转数字的过程中,JavaScript 做了同样的事情,只是它会首先尝试 valueOf 方法
- 如果对象具有 valueOf 方法,且返回一个原始值,则 JavaScript 将这个原始值转换为数字并返回这个数字
- 否则,如果对象具有 toString 方法,且返回一个原始值,则 JavaScript 将其转换并返回。
- 否则,JavaScript 抛出一个类型错误异常。
console.log(Number({})) // NaN
console.log(Number({a : 1})) // NaN
console.log(Number([])) // 0
console.log(Number([0])) // 0
console.log(Number([1, 2, 3])) // NaN
console.log(Number(function(){var a = 1;})) // NaN
console.log(Number(/\d+/g)) // NaN
console.log(Number(new Date(2010, 0, 1))) // 1262275200000
console.log(Number(new Error('a'))) // NaN
注意,在这个例子中,[]
和 [0]
都返回了 0,而 [1, 2, 3]
却返回了一个 NaN。我们分析一下原因:
当我们 Number([])
的时候,先调用 []
的 valueOf
方法,此时返回 []
,因为返回了一个对象而不是原始值,所以又调用了 toString
方法,此时返回一个空字符串,接下来调用 ToNumber
这个规范上的方法,参照对应表,转换为 0
, 所以最后的结果为 0
。
而当我们 Number([1, 2, 3])
的时候,先调用 [1, 2, 3]
的 valueOf
方法,此时返回 [1, 2, 3]
,再调用 toString
方法,此时返回 1,2,3
,接下来调用 ToNumber
,参照对应表,因为无法转换为数字,所以最后的结果为 NaN
。
# 7、类型转换的面试题
# 7.1 定义一个变量a,使if (a == 1 && a == 2 && a == 3)条件成立
const a = {
value: 0,
valueOf() {
++this.value;
return this.value;
},
/* toString() {
++this.value;
return this.value;
} */
};
// 要点就是这里必须是looseEqual, 才会进行类型隐式转换
if (a == 1 && a == 2 && a == 3) {
console.log(a);
}