# 类型转换

让我们思想一个问题,类型转换是邪恶的吗?带着这个问题来看。

# 强制类型转换

某些情况下,我们希望将值显示的转换为我们期望的类型。

# 字符串的转换 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

  1. if () 语句中的测试表达式
  2. for () 的第二个字句
  3. while () 的循环测试表达式。
  4. ?: 三元表达式中的第一个子句。
  5. || && 操作符的左手边的操作数

任何不适 boolean 的值,都会调用 ToBoolean。

注意:

||&& 会将所有布尔值的都为 falsefunction foo(a) {
    a = a || 'hello';  // 不好使,我传的是 空字符串
}
foo('');
这时候,我们可以使用 ?? ,只要不是 undefinednull 才会通过检测。

# 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) {} 

# 安全使用建议

  1. 如果比较的任意一边可能出现true或者false值,那么就永远,永远不要使用==。
  2. 如果比较的任意一边可能出现[],"",或0这些值,那么认真地考虑不使用==