1、显示类型转换

显示类型转换顾名思义就是用特有的方法对类型进行转换
显示类型转换的方式只有三种:

1、Number()

  • 原始类型转换

    • 数值:转换后还是数值
    • 字符串:如果可以被解析为数值,则转化为相应的数值会忽略所有前导的 0,否则得到 NaN,空字符串为 0
    1
    Number("000001"); //1
    • 布尔值:true 转成 1,false 转成 0
    • undefined:转成 NaN
    • null:转成 0
  • 对象类型转换

    • 先调用对象自身的 valueOf 方法,如果该方法返回原始类型的值(数值、字符串和布尔值),则直接对该值使用 Number 方法,不再进行后续步骤
    • 如果 valueOf 方法返回复合类型的值,再调用对象自身的 toString 方法,如果 toString 方法返回原始类型的值,则对该值使用 Number 方法,不再进行后续步骤
    • 如果 toString 方法返回的是复合类型的值,则报错
  • parseInt 和 parseFloat

    • parseInt:只解析整数,当传入的值以‘0x’或‘0X’开头时,会解析成十六进制数
    • parseFloat:既可以解析整数又可以解析浮点数
    • 共同点:都会跳过任意数量的前导空格,尽可能解析更多数值字符,并忽略后面的内容。如果第一个非空格字符是非法的数字直接量,将最终返回 NaN

2、String()

  • 原始类型转换

    • 数值:转为相应的字符串
    • 字符串:转换后还是原来的值
    • 布尔值:true 转为‘true’,false 转为‘false’
    • undefined:转为‘undefined
    • null:转为‘null’
  • 对象类型转换

    • 先调用 toString 方法,如果 toString 方法返回的是原始类型的值,则对该值使用 String 方法,不再进行以下步骤
    • 如果 toString 方法返回的是复合类型的值,再调用 valueOf 方法,如果 valueOf 方法返回的是原始类型的值,则对该值使用 String 方法,不再进行以下步骤
    • 如果 valueOf 方法返回的是复合类型的值,则报错
  • JSON.stringify()
    JSON.stringify 底层也是调用了 toString 方法

3、Boolean()

undefined、null、-0、+0、NaN、’’(空字符串)返回的都是 false,除了上述的都是返回 true,包括对象类型

2、隐式类型转换

隐式类型转换同样有三种:

1、+(加号)

1.1 一元+(加号)运算符

查看ES5 规范 11.4.6可以看到,当+(加号)作为一元运算符时,底层会调用 ToNumber 方法去处理,相当于 Number(),当+(加号)后是普通数值时,返回的就是 Number(数值)

1
+"1"; //1

当+(加号)后是对象类型时,对象类型就会像显示类型中所讲:

  • 先调用对象自身的 valueOf 方法,如果该方法返回原始类型的值(数值、字符串和布尔值),则直接对该值使用 Number 方法,不再进行后续步骤
  • 如果 valueOf 方法返回复合类型的值,再调用对象自身的 toString 方法,如果 toString 方法返回原始类型的值,则对该值使用 Number 方法,不再进行后续步骤
  • 如果 toString 方法返回的是复合类型的值,则报错
1
2
+[]; //0  []先调用valueof方法返回[],再调用toString方法返回""
+{}; //NaN {}相当于new Object() 先调用valueof方法返回{},再调用toString方法返回"[object Object]"

1.2 二元+(加号)运算符

查看ES5 规范 11.6.1可以看到当 value1+value2 时:

1. lprim = ToPrimitive(value1)
2. rprim = ToPrimitive(value2)
3. 如果 lprim 和 rprim 都是 String 类型时,则直接返回 ToString(lprim)和 ToString(rprim)拼接结果
4. 返回 ToNumber(lprim) 和 ToNumber(rprim)的运算结果

ToPrimitive、ToString、ToNumber 三者皆为内部函数:

  • ToPrimitive 执行的步骤:

    1. 如果 obj 为基本类型,直接返回
    2. 否则,调用 valueOf 方法,如果返回一个原始值,则 JavaScript 将其返回。
    3. 否则,调用 toString 方法,如果返回一个原始值,则 JavaScript 将其返回。
    4. 否则,JavaScript 抛出一个类型错误异常。
  • ToString 相当于 String()

  • ToNumber 相当于 Number()

按上述规范,对下列几个例子进行讲解:

1
2
3
4
5
null + 1; //1
//1、lprim = ToPrimitive(null) 因为null为基本数据类型,所以返回null
//2、lprim = ToPrimitive(1) 因为1为基本数据类型,所以返回1
//3、lprim和lprim都不是String类型,所以执行第四部
//4、ToNumber(null)+ToNumber(1),所以返回为1
1
2
3
4
5
undefined + 1; //NaNnull
//1、lprim = ToPrimitive(undefined) 因为undefined为基本数据类型,所以返回undefined
//2、lprim = ToPrimitive(1) 因为1为基本数据类型,所以返回1
//3、lprim和lprim都不是String类型,所以执行第四部
//4、ToNumber(undefined)+ToNumber(1),所以返回为NaN
1
2
3
4
[] + []; //''
//1、lprim = ToPrimitive([]) []的valueof为[]不是基本数据类型,再调用toString方法返回的是''
//2、lprim = ToPrimitive([]) []的valueof为[]不是基本数据类型,再调用toString方法返回的是''
//3、lprim和lprim都是String类型,所以直接返回拼接,结果为''
1
2
3
4
[] + {}; //'[object Object]'
//1、lprim = ToPrimitive([]) []的valueof为[]不是基本数据类型,再调用toString方法返回的是''
//2、lprim = ToPrimitive({}) {}的valueof为{}不是基本数据类型,再调用toString方法返回的是'[object Object]'
//3、lprim和lprim都是String类型,所以直接返回拼接,结果为'[object Object]'
1
2
3
{} + []; //0
//按照规范来说,这个的结果应该和上一个一样,但是为什么会出现如此的不同,是因为在浏览器中,{}相当于一个独立的代码块,所以浏览器认为{}+[]相当于+[],所以返回0
//如果想返回和上一个一样的结果的话可以在表达式外加括号,({} + [])这样就返回正常的结果了
1
{} + {}; //谷歌返回'[object Object][object Object]' 火狐返回NaN

2、比较运算

2.1 ==相等

同样我们查看ES5 规范 11.9.3中所讲:
比较 x == y,其中 x 和 y 是值,产生 true 或 false。这样的比较执行如下:

1. 如果 x,y 类型相同,则
  a. 如果 x 是 Undefined,则返回 true。
  b. 如果 x 为 Null,则返回 true。
  c. 如果 x 是数字,则
      i. 如果 x 为 NaN,则返回 false。
      ii. 如果 y 为 NaN,则返回 false。
      iii. 如果 x 与 y 相同,则返回 true。
      iv. 如果 x 为+0 且 y 为 − 0,则返回 true。
      v. 如果 x 为 − 0 且 y 为+0,则返回 true。
      vi.返回 false。
  d. 如果 x 类型为 String,则如果 x 和 y 是完全相同的字符序列(相同的长度和相同位置的相同字符),则返回 true。否则,返回 false。
  e. 如果 x 类型为布尔值,则如果 x 和 y 均为 true 或均为 false,则返回 true。否则,返回 false。
  f. 如果 x 和 y 指向同一对象,则 返回 true。否则,返回 false。
2. 如果 x 是 Null 和 y 是 Undefined 的,返回 true。
3. 如果 X 是 Undefined 和 y 为 Null,返回 true。
4. 如果 x 类型是 Number 且 y 类型是 String,则返回比较结果 x == ToNumber(y)。
5. 如果 x 类型为 String 且 y 类型为 Number,则返回比较结果 ToNumber(x)== y。
6. 如果 x 类型为布尔值,则返回比较结果 ToNumber(x)== y。
7. 如果 y 类型为布尔型,则返回比较结果 x == ToNumber(y)。
8. 如果 x 类型是 String 或 Number 且 y 类型是 Object,则返回比较结果 x == ToPrimitive(y)。
9. 如果 x 类型是 Object 并且 y 类型是 String 或 Number,则返回比较结果 ToPrimitive(x)== y。
10. 返回 false。

按上述规范,对下列几个例子进行讲解:

1
2
3
undefined == undefined; //true
null == null; //ture
//查看上述的1.a、1.b条
1
2
NaN == NaN; //false
//查看上述的1.c.i条
1
2
3
undefined == null; //true
null == undefined; //ture
//查看上述的2、3条
1
2
3
1 == "1"; //true
"1" == 1; //ture
//查看上述的4、5条
1
2
3
false == 0; //true
0 == false; //ture
//查看上述的6、7条
1
2
3
[] == 0; //true
0 == []; //ture
//查看上述的8、9条

2.2 <判断

同样我们查看ES5 规范 11.8.5中所讲:
比较 x < y(其中 x 和 y 是值)会产生 true,false 或 undefined(这表明至少一个操作数是 NaN)。除 x 和 y 外,该算法还使用名为 LeftFirst 的布尔标志 作为参数。该标志用于控制对 x 和 y 进行具有潜在可见副作用的操作的顺序 。这是必需的,因为 ECMAScript 指定了从左到右的表达式求值。LeftFirst 的默认值 为 true,表示 x 参数对应于 y 参数对应表达式左侧出现的表达式。如果 LeftFirst 为假,则相反,操作必须在 x 之前对 y 执行。这样的比较执行如下:

1. 如果LeftFirst标志为true,则
  a. 设 px为调用ToPrimitive的结果(x,Number)。
  b. 令 py为调用ToPrimitive的结果(y,Number)。
2. 否则需要颠倒评估顺序以保留从左到右的评估
  a. 令 py为调用ToPrimitive的结果(y,Number)。
  b. 设 px为调用ToPrimitive的结果(x,Number)。
3. 如果px和py都不是String类型,则
  a. 令 nx为调用ToNumber(px)的结果。因为px和py是原始值,所以评估顺序并不重要。
  b. 令 ny为调用ToNumber(py)的结果。
  c. 如果 nx是NaN,则返回undefined。
  d. 如果 ny是NaN,则返回undefined。
  e. 如果 nx和ny是相同的Number值,则返回false。
  f. 如果 nx为+0且ny为− 0,则返回false。
  g. 如果 nx为− 0 且ny为+0,则返回false。
  h. 如果 nx为+ ∞,则返回false。
  i. 如果 ny为+ ∞,则返回true。
  j. 如果 ny为-∞,则返回false。
  k. 如果 nx为-∞,则返回true。
  l. 如果nx的数学值小于ny的数学值(请注意,这些数学值都是有限的且不都是零),则返回true。否则,返回 false。
4. 否则,px和py都是字符串
  a. 如果 py是px的前缀,则返回false。(如果q 可以是连接p和其他String r的结果,则String值p是String值q的前缀。请注意,任何String都是其自身的前缀,因为r 可能是空String。)
  b. 如果 px是py的前缀,则返回true。
  c. 令 k为最小的非负整数,以使px内的位置k处的字符不同于py内的位置k处的字符。(必须有一个k,因为String都不是另一个的前缀。)
  d. 设m为整数,表示字符在px内位置k处的代码单位值。
  e. 设n为整数,表示字符在py中位置k处的代码单位值。
  f. 如果 m < n,则返回true。否则,返回 false。

按上述规范,对下列几个例子进行讲解:

1
2
[] < []; //false
// 以我理解为首先LeftFirst发现左边为false,而后颠倒发现左边依旧为false,则相当于fasle<fasle 最后结果为fasle
1
2
[] < {}; //ture
// 相当于''和'[object Object]'进行比较,则明显空字符串小

参考:
冴羽:JavaScript 深入之头疼的类型转换(上)
冴羽:JavaScript 深入之头疼的类型转换(下)