# 类型转换
让我们思想一个问题,类型转换是邪恶的吗?带着这个问题来看。
# 强制类型转换
某些情况下,我们希望将值显示的转换为我们期望的类型。
# 字符串的转换 ToString
原始值需要借助内建类型(原生类型), String() 来将一个非字符串类型强制转换为 string , String() 转换的过程是由语言规范ToString 抽象操作处理的。
| 参数类型 | 结果 |
|---|---|
| Undefined | "undefined" |
| Null | "null" |
| Boolean | 如果参数是 true,返回 "true"。参数为 false,返回 "false" |
| Number | 又是比较复杂,可以看例子 |
| String | 返回与之相等的值 |
# 布尔值的转换 ToBoolean
原始值需要借助内建类型(原生类型), Boolean() 来将一个非字符串类型强制转换为 boolean , Boolean() 转换的过程是由语言规范 ToBoolean 抽象操作处理的。
| 参数类型 | 结果 |
|---|---|
| false | false |
| undefined | false |
| null | false |
| +0 | false |
| -0 | false |
| NaN | false |
| '' | false |
除了这六种转为 false, 其余的都是为 true。
# 数值的转换 ToNumber
原始值需要借助内建类型(原生类型), Number() 来将一个非字符串类型强制转换为 number , Number() 转换的过程是由语言规范 ToNumber 抽象操作处理的。
| 参数类型 | 结果 |
|---|---|
| Undefined NaN | |
| Null | +0 |
| Boolean | 如果参数是 true,返回 1。参数为 false,返回 +0 |
| Number | 返回与之相等的值 |
| String | 这段比较复杂,看例子 |
Number 来转换一个字符串,会将其转为一个整数或浮点数,会忽略所有前导的 0, 如果有一个字符不是数字,结果都会返回 NaN。
# 原始值转对象 (看包装对象章节).
# 对象转原始值
js 有两个不同的方法来执行转换,一个是 toString,一个是 valueOf。(真实报漏出来的)。
Object.prototype.toString 方法可以返回 [[class]] 内部属性。
而 js 下的很多类根据自己的特点,定了更多版本的 toString。
- 数组的 toString 返回一个字符串,逗号隔开。
- 函数的 toString 方法返回源代码字符串。
- 日期的 toString 方法返回一个可读的日期和时间字符串。
- RegExp 的 toString 返回一个表示正则表达式直接量的字符串。
valueOf 表示对象的原始值,默认的 valueOf 返回对象本身。日期会返回毫秒数。
是利用 ToPrimitive 方法,输入一个值,返回一个基本类型的值。
ToPrimitive(input[, PreferredType])
它接收两个参数,第一个参数是 input,表示要处理的值。
第二个参数是 PreferredType, 表示希望转成的类型,可以是 Number、String。
如果不传入第二个 PreferredType, 如果 input 是日期类型,相当于传入 String,其余都是传入 Number。
如果传入的 input 是 null, undefined, string, number, boolean 会直接返回该值。
如果是 ToPrimitive(obj, Number).
- 如果 obj 为 基本类型,直接返回
- 否则,调用 valueOf 方法,如果返回一个原始值,则 JavaScript 将其返回。
- 否则,调用 toString 方法,如果返回一个原始值,则 JavaScript 将其返回。
- 否则,JavaScript 抛出一个类型错误异常。
如果是 ToPrimitive(obj, String),处理步骤如下:
- 如果 obj为 基本类型,直接返回
- 否则,调用 toString 方法,如果返回一个原始值,则 JavaScript 将其返回。
- 否则,调用 valueOf 方法,如果返回一个原始值,则 JavaScript 将其返回。
- 否则,JavaScript 抛出一个类型错误异常。
# 隐式类类型转换
对于你来说,只要不是明确的类型转换都可以叫做它是隐式转换。
# 隐含的: String <-> Number
字符串和数字的转换
var a = '43';
var b = '0';
var c = 43;
var d = 0;
a + b; // '430'
c + d; // 43
二元加法运算符, 当其中一个已经是字符串了,就会进行加法拼接。
var a = [1, 2];
var b = [3, 4];
a + b; // '1,23,4'
但如果其中一个是对象,就会调用 ToPrimitive 抽象操作。带着 number 上下文来调用算法。valueOf() 返回对象本身,继续调用 toString() 返回一个逗号隔开的字符串。
我们可以利用只要有一方是字符串,就会进行字符串拼接,来进行数字转字符串
var a = 42;
var b = a + '';
b; // '42'; // 这种隐式转换还是很方便的。
但是注意:下面的两种转换结果是不一样的,因为它们带有的上下文是不一样的
除非你自定义了对象的操作,不然下面情况是很少能够干扰到你的。
更多常见的是 通过隐式这种来转换的。
var obj = {
valueOf() {
return 1;
},
toString() {
return 2;
}
}
obj + ''; // 隐式 1
String(obj); // 明确 2
# 隐含的: Boolean <-> Number
boolean 值到 number 会被转为 0或1. 这种转换可以让我们简单的做一些操作。
sum += argument[i] // 会转为数字
# 隐含的: * -> Boolean
- if () 语句中的测试表达式
- for () 的第二个字句
- while () 的循环测试表达式。
- ?: 三元表达式中的第一个子句。
- || && 操作符的左手边的操作数
任何不适 boolean 的值,都会调用 ToBoolean。
注意:
||、&& 会将所有布尔值的都为 false。
function foo(a) {
a = a || 'hello'; // 不好使,我传的是 空字符串
}
foo('');
这时候,我们可以使用 ?? ,只要不是 undefined 和 null 才会通过检测。
# Symbol 明确转换
从一个symbol到一个string的 明确 强制转换是允许的. 但是相同的 隐含 强制转换是不被允许的,而且会抛出一个错误。
var s1 = Symbol('cool');
String(s1); // 'Symbol(cool)'
var s2 = Symbol('not');
s2 + ''; // TypeError
Symbol 可以明确或者隐含的转为 Boolean, 总是 true;
# 双等于
var a = 42;
var b = '42';
a == b; // true
如果Type(x)是Number而Type(y)是String, 返回比较x == ToNumber(y)的结果。
如果Type(x)是String而Type(y)是Number, 返回比较ToNumber(x) == y的结果。
var a = 1;
var b = true;
a == b; // true
如果Type(x)是Boolean, 返回比较 ToNumber(x) == y 的结果。
如果Type(y)是Boolean, 返回比较 x == ToNumber(y) 的结果。
所以我们在用 true/false 的时候,要使用严格等于。
var a = '42';
if(a == true) {} // 不好,会失败的
if (a === true) {} // 不好, 会失败
if (a) {} // 能够隐含的转换
if (!!a) {} //明确的工作
if (Boolean(a)) {} // 明确的工作
null == undefined;
如果x是null而y是undefined,返回true。
如果x是undefined而y是null,返回true。
比较:object与非object
如果Type(x)是一个String或者Number而Type(y)是一个Object, 返回比较 x == ToPrimitive(y) 的结果。
如果Type(x)是一个Object而Type(y)是String或者Number, 返回比较 ToPrimitive(x) == y 的结果。
疯狂的情况
[] == ![]; // true -> [] == false 0 = 0
2 == [2] // true
'' == [null] ; // true '' == ''
0 == '\n'; // true
我们应该注意的(常会踩坑的)
'' == 0; // 0 == 0;
'' == []; // '' == 0 0 == 0
0 == []; // 0 == 0
下面的这种,我们应该避免使用,以防止我们踩坑。
if (a == '') {}
if (a == b) {}
# 安全使用建议
- 如果比较的任意一边可能出现true或者false值,那么就永远,永远不要使用==。
- 如果比较的任意一边可能出现[],"",或0这些值,那么认真地考虑不使用==
← 为什么出现BigInt 包装类型 →