JavaScript 对象转换之 toString 和 valueOf
开始这个话题之前,我们先看一下下面的几个例子:
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转换,具体规则如下:
- 如果
toString
方法存在并且返回“原始类型”,返回toString的结果。 - 如果toString方法不存在或者返回的不是“原始类型”,调用
valueOf
方法,如果valueOf方法存在,并且返回“原始类型”数据,返回valueOf的结果。 - 其他情况,抛出错误。
我们可以用下面的方法简单的证明上面的规则:
首先我们尝试改写一个对象的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转换
- 方法参数需要Number的时候,如Math.sin(obj)等
- 对比的时候,如 obj == 'abc'
- 运算的时候,如 obj + 123
转换规则如下:
- 如果valueOf存在,且返回“原始类型”数据,返回valueOf的结果。
- 如果toString存在,且返回“原始类型”数据,返回toString的结果。
- 报错。
可以参考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]"
关于这些转换,有时确实很让人费解,不过在了解原理之后,就会变得很容易了。有什么问题,可以给我留言,我会第一时间回复。