开始这个话题之前,我们先看一下下面的几个例子:

parseInt(0.0000004)  // 4
![]==[] //true
['x','y'] == 'x,y' //true
alert({name:'mofei'})  //"[object Object]"

结果有时候会让我大吃一惊,这是为什么呢?今天就和大家讨论一下JavaScript的对象转换。

基础类型

先让我们了解一下JavasScript转换的“原始类型(primitive value)”:

Number

String

Boolean

在JavaScript进行对比或者各种运算的时候会把对象转换成这些类型,从而进行后续的操作,下面逐一说明:

String转换

在某个操作或者运算需要字符串的时候,往往会触发Object的String转换,举个例子

var obj={name:'Mofei'}
var str = ' ' + obj
console.log(str);  //   [object Object]

上述的例子中,在字符串相加的过程中,系统调用了obj的String转换,具体规则如下:

  1. 如果toString方法存在并且返回“原始类型”,返回toString的结果。
  2. 如果toString方法不存在或者返回的不是“原始类型”,调用valueOf方法,如果valueOf方法存在,并且返回“原始类型”数据,返回valueOf的结果。
  3. 其他情况,抛出错误。

我们可以用下面的方法简单的证明上面的规则:

首先我们尝试改写一个对象的toString方法

var a={
    toString:function(){
        console.log('调用了 a.toString');
        return '111';
    }
}
alert(a);

//调用了 a.toString
// 111

可以看到,系统在执行 ' '+a 的时候,自动调用了a的toString方法,将a(Object)转换成了String。

下面我们尝试证明如果toString()方法不可用的时候系统会调用valueOf()方法

var a={
    toString:function(){
        console.log('调用了 a.toString');
        return '111';
    },
    valueOf:function(){
        console.log('调用了 a.valueOf');
        return '111';
    }
}
alert(a);

//调用了 a.toString

这里我们增加了valueOf方法,但是发现系统并没有调用,这是因为,第一步toString返回的是原始类型,我们尝试把第一步返回的值改成一个对象 {}

var a={
    toString:function(){
        console.log('调用了 a.toString');
        return {};
    },
    valueOf:function(){
        console.log('调用了 a.valueOf');
        return '111';
    }
}
alert(a);

// 调用了 a.toString
// 调用了 a.valueOf

从结果可以看到,当toString不可用的时候,系统会再尝试valueOf方法,我们继续修改valueOf方法,把valueOf方法也改成返回对象 {}

var a={
    toString:function(){
        console.log('调用了 a.toString');
        return {};
    },
    valueOf:function(){
        console.log('调用了 a.valueOf');
        return {};
    }
}
alert(a);

// 调用了 a.toString
// 调用了 a.valueOf
//  Uncaught TypeError: Cannot convert object to primitive value

可以发现,如果toString和valueOf方法均不可用的情况下,系统会直接返回一个错误。

Number转换

下面说说Number转换,同理,当需要使用Number时,( 如Math.sin() )等,解释器会尝试将对象转换成Number对象。

通常有如下的情况会触发Number转换

  1. 方法参数需要Number的时候,如Math.sin(obj)等
  2. 对比的时候,如 obj == 'abc'
  3. 运算的时候,如 obj + 123

转换规则如下:

  1. 如果valueOf存在,且返回“原始类型”数据,返回valueOf的结果。
  2. 如果toString存在,且返回“原始类型”数据,返回toString的结果。
  3. 报错。

可以参考String转换的方法进行验证,这里只列出一种典型的方法,其他的可以自己动手来修改

var a={
    toString:function(){
        console.log('调用了 a.toString');
        return 12;
    },
    valueOf:function(){
        console.log('调用了 a.valueOf');
        return {};
    }
}
a+1
//调用了 a.valueOf
//调用了 a.toString
//13

可以看到,这里我们改写了valueOf和toString方法,系统在调用valueOf方法之后发现返回的不是“原始类型”数据,于是又尝试调用了toString方法,并返回了该方法返回的值12,最后+1变成了13。

Boolean转换

在进行布尔比较的时候,比如 if(obj) , while(obj)等等,会进行布尔转换,布尔转换遵循如下规则:

布尔值
true/false true/false
undefined,null false
Number 0,NaN 对应 false, 其他的对应 true
String ""对应false,其他对应true('0'对应的是true)
Object true

举个比较典型的例子

[] == ![]  //true

// 首先第一步右边的是逻辑判断![],说以先转成boolean
// [] == !true
// [] == false
// 左边不是原始类型,尝试把左边转成原始类型,变成
// '' == false
// 转成Number
// 0 == 0

总结

通过上述的介绍,一开始提到的问题应该都很容易得到答案了。

parseInt(0.0000004)  // 4
![]==[] //true
['x','y'] == 'x,y' //true
alert({name:'mofei'})  //"[object Object]"

关于这些转换,有时确实很让人费解,不过在了解原理之后,就会变得很容易了。有什么问题,可以给我留言,我会第一时间回复。