你的浏览器不支持canvas

Love You Ten Thousand Years

强制类型转换

Date: Author: M/J

本文章采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。转载请注明来自小可嗒嗒的博客

在介绍强制转换之前,不论是显式的还是隐式的,我们需要了解字符串、数字和布尔值之间类型转换的基本规则,这些规则(也叫抽象操作)是仅供内部使用的。

toString

抽象操作toString负责处理非字符串到字符串的强制类型转换。基本类型值得字符串化规则是:null转换成"null"undefined转换为"undefined"true转换成"true"。数字的字符串化则遵循通用规则。

toNumber

抽象操作toNumber处理非数字值到数字值的强制类型转换。规则如下:

  • true变成1false变成0undefined转换成NaNnull变成0
  • 对字符串的处理基本遵循数字常量的相关规则和语法,处理失败返回NaN
  • 对象会首先转换为相应的基本类型值,如果返回的是非数字的基本类型值,则再遵循以上规则将其强制转换为数字。

为了将值转换为相应的基本类型值,抽象操作ToPrimitive首先通过内部操作DefaultValue检查改制是否有valueOf()方法,如果有并且返回基本类型值,就使用该值进行强制类型转换。如果灭有就使用toString()的返回值来进行强制类型转换。如果两者都没有,产生TypeError错误。

to Boolean

假值,即

  • undefined
  • null
  • false
  • +0-0NaN
  • ""

都将强制转换为false

就算是封装了价值的封装对象,如:

var a = new Boolean(false),
    b = new Number(0),
    c = new String("");

abc的布尔强制转换都是true,因为所有的对象都是真值。

上述三个操作包括toPrimitive就是JavaScript处理类型转换的规则,有了他们,就可以更好地了解强制类型转换。

显式强制类型转换

字符串与数字之间的显式转换

字符串和数字之间的转换是通过String()Number()这两个内建函数,即原生函数来实现的。

var a = 42,
    b = String(a),
    c = "3.14",
    d = Number(c);

console.log(b); //"42"
console.log(d); //3.14

注意,它们前面没有new关键字,并不是创建封装对象。但var e = Array(1,2,3)这个语句是构造一个数组对象,new会被自动添加。

String()遵循前面讲的toString规则,Number()遵循前面的toNumber规则。

除了上面的方法外,还有方法可以实现两者的显式转换。

var a = 42,
    b = a.toString(),
    c = "3.14",
    d = +c;

a.toString()是显式的,不过其中涉及隐式转换。因为toString()对象42这种基本类型值不适用,所以会自动为42创建一个封装对象,从而调用toString()方法。值得注意的是,我们没有toNumber()方法。对于d = +c是通过一元运算符显式地将c转换为数字,而非数字加法运算,也不是字符串拼接。至于说这是隐式还是显式,取决于个人理解。如果你知道这是一元运算符的形式,那就是显式。如果你不知道,你说是隐式也没有关系。但在开源社区里,一元运算符+普遍认为是显式。

日期显式转换为数字

一元运算符的另外一个用途是将日期Date对象强制类型转换为数字,返回Unix时间戳,以微秒为单位(即从1970年1月1日00:00:00 UTC到当前时间):

var timestamp = +new Date();

当然,对于提取时间戳我们有更好的方法。

var timestamp = new Date().getTime();

或者使用ES5中新加入的静态方法Date.now()来获取当前的时间戳。

var timestamp = Date.now();

显式解析数字字符串

使用parseInt()方法将字符串解析为整数,parseFloat()方法将字符串解析为浮点数。这和将字符串强制类型转换为数字类似但不同的地方在于,解析允许字符串中含有非数字字符

var a = "42px";

Number(a); //NaN
parseInt(a); //42

显式转换为布尔值

与前面的String()Number()一样,Boolean()是显式的toBoolean强制类型转换。

虽然Boolean()是显式的,但并不常用。大家都喜欢使用一元运算符!将值强制转换为布尔值,但同时它也会将假值变为真值,将真值变为假值,所以,你可以使用!!来做判断。

var a = {},
    b = [];

!!a; //true
!!b; //true

if()语句中,如果没有使用Boolean()!!,就会自动隐式进行toBoolean转换。


隐式强制类型转换

字符串和数字之间的隐式类型转换

+运算符既能用于数字加法,用能用于字符串拼接。如果+的其中一个操作数是字符串(或者通过一定步骤得到的字符串),则执行字符串拼接操作。

var a = "42",
    b = 0,
    c = [1,2],
    d = [3,4];

a + b; //"420"
c + d; //"1,23,4"

"1,23,4"是怎么来的呢,这就是通过一定步骤得到的字符串。在ES5规则中,如果其中一个操作数是对象(当然包括数组),则首先对其调用toPrimitive抽象操作,该抽象操作再调用[[DefaultValue]]。因为数组的ValueOf()操作无法得到简单的基本类型值,于是它转而调用toString(),因此两个数组变成了"1,2""3,4"+将她们拼接后返回1,23,4

依上,可以将数字和空字符串""+来将其转换为字符串。

var a = 42,
    b = a + "";
b; //"42"

a + ""与显式的String(a)的区别在于,a + ""会对a调用valueOf()方法,然后通过toString操作将返回值转换为字符串,而String()则直接调用toString抽象操作。所以,在定制valueOf()toString()方法时就需要特别小心。

因为-运算符是不会用于字符串操作的,所以可使用-将字符串强制类型转换为数字。

var a = "3.14",
    b = a - 0;
b; //3.14

隐式强制类型转换为布尔值

  • if()语句的条件判断表达式
  • for(..;..;..)语句中的条件判断表达式
  • while()do...while()循环中的条件判断表达式
  • ?:中的条件判断表达式
  • ||&&左边的操作数

这里特地讲一下||&&,这与其他语言(CPHP)中的完全不同。在JavaScript返回值是两个操作数其中的一个。

var a = 42,
    b = "abc",
    c = null;

a || b; //42
c || b; //"abc"

a && b; //"abc"
c && b; //null

||&&首先会对第一个操作数执行条件判断,如果其不是布尔值就先进行toBoolean强制类型转换,再执行条件判断。

  • ||来说,如果条件判断结果为true就返回第一个操作数的值,如果为false就返回第二个操作数的值。

  • &&来说,刚好相反。判断条件为true就返回第二个操作数的值,如果为false就返回第一个操作数的值。

所以,基于上述原理,我们有了一种设置默认值的方法,很是方便。

function foo(a,b){
    a = a || "hello";
    b = b || "world";

    console.log(a + " " + b);
}
foo (); // "hello world"
foo ("yeah", "yeah!"); //"yeah yeah!"

&&常用语JavaScript代码压缩工具。

function foo(){
    console.log(a);
}
var a  = 42;
a && foo();

foo()只有在条件判断a通过是才会被调用,如果为通过,a && foo()就会短路foo()不会被调用。开发人员通常会使用if (a){foo();}这种写法,但压缩工具通常使用a && foo(),因为更简洁。


== vs ===

宽松相等==和严格相等===用来判断两个值是否相等

正确的解释是:==允许在相等比较中进行强制类型转换,而===不可以

有些人会认为说==检查值是否相等,===检查值和类型是否相等。这是不正确的理解。

ES5规范中使用抽象相等比较算法定义了==运算符的行为。

  • 如果两个值的类型相同,仅比较他们是否相等
  • 两个对象指向同一值时,即视为相等,不发生强制类型转换
  • 两个不同类型的值是会发生隐式强制类型转换

特殊例子有:NaN不等于NaN+0不等于-0

字符串和数字之间的相等比较

ES5规范11.9.3.4-5这样定义:

  • 如果Type(x)是数字,Type(y)是字符串,则返回x == toNumber(y)的结果
  • 如果Type(x)是字符串,Type(y)是数字,则返回toNumber(x) == y的结果

其他类型和布尔类型之间的相等比较

ES5规范11.9.3.6-7这样定义:

  • 如果Type(x)是布尔类型,则返回toNumber(x) == y的结果
  • 如果Type(y)是布尔类型,则返回x == toNumber(y)的结果

null和undefined之间的相等比较

ES5规范11.9.3.2-3这样定义:

  • 如果xnullyundefined,则结果为true
  • 如果xundefinedynull,则结果为true

对象与非对象之间的相等比较

ES5规范11.9.3.8-9这样定义:

  • 如果Type(x)是数字或字符串,Type(y)是对象,则返回x == toPrimitive(y)的结果
  • 如果Type(x)是对象,Type(y)是数字或字符串,则返回toPrimitive(x) == y的结果

编辑备注:

  • 2017-04-17第一次编辑

对于本文内容有问题或建议的小伙伴,欢迎在文章底部留言交流讨论。