toString和valueOf区别

398 阅读4分钟

toString()和valueOf()是Object原型上的方法,所以每个对象都存在这两个方法,toString返回的是对象的字符串、而valueOf是返回对象的原始值,而有些内置对象会重写这些方法(如Date对象、Array对象等)

隐式调用规则:

  • 使用操作符运算时(如==、+-*/等),调用valueOf方法,获取对象的值;
  • 调用alert、confirm时,则调用toString()

强制类型:使用String()则调用toString、使用Number()则调用valueOf方法;

var a={
    toString:function(){
        return 0
    },
    valueOf:function(){
        return 1
    }
}

console.log(a,a.toString(),a.valueOf())
//{...},"[object Object]",{...}
console.log(a+"12")//112 调用valueOf
console.log(a==0)//false 调用valueOf
console.log(a==1)//true 调用valueOf
console.log(a<1)//false 调用valueOf
console.log(a>0)//true 调用valueOf
console.log(String(a))//0 调用toString
console.log(Number(a))//1 调用valueOf
alert(a)//0 调用toString
confirm(a)//0 调用toString

关于优先级,不少文章写到重写方法会提高优先级,但根据我的测试发现,就算重写,也是根据valueOf和toString的返回值判断,且两个方法可互相替代,首先会根据默认规则,但当返回值为对象时,则会调用另一方法替代,当都为对象时,则会报错。

var a={}

console.log(a,a.toString(),a.valueOf())
//{},"[object Object]",{}
console.log(a+"12")//[object Object]12 调用toString
console.log(a==0)//false 调用toString
console.log(a==1)//false 调用toString
console.log(a<1)//false 调用toString
console.log(a>0)//false 调用toString
console.log(String(a))//[object Object] 调用toString
console.log(Number(a))//NaN 调用toString
alert(a)//[object Object]
confirm(a)//[object Object]

var a=[]
console.log(a,a.toString(),a.valueOf())
//[],"",[]
console.log(a+"12")//12 调用toString
console.log(a==0)//true 调用toString,再隐式转化类型
console.log(a==1)//false 调用toString,再隐式转化类型
console.log(a<1)//true 调用toString,再隐式转化类型
console.log(a>0)//false 调用toString,再隐式转化类型
console.log(String(a))// 调用toString
console.log(Number(a))//0 调用toString
alert(a)// 调用toString
confirm(a)// 调用toString

上面代码中,对象a的toString返回字符串,valueOf返回a对象,所以会调用toString替代valueOf,下面再来个例子,只重写toString方法,看看是否有提升优先级,从而替代valueOf?下面来测试一下

var a = new Boolean(1)
a.toString=function(){
    return false
}

console.log(a,a.toString(),a.valueOf())
//Boolean {true, toString: ƒ},false,true  toString和valueof均返回boolean值
console.log(a+"12")//true12 调用valueOf
console.log(a==0)//false 调用valueOf
console.log(a==1)//true 调用valueOf
console.log(a<1)//false 调用valueOf
console.log(a>0)//true 调用valueOf
console.log(String(a))//false 调用toString
console.log(Number(a))//1 调用valueOf
alert(a)//false 调用toString
confirm(a)//false 调用toString

可见,重写并不会提升优先级,那么什么时候toString会替代valueOf,或者valueOf会替代toString呢,通过下面例子会发现,若调用对应的方法返回值为引用类型,则会调用另一方法

var a = new Number(2)
a.toString=function(){
    return 1
}
a.valueOf=function(){
    return []
}

console.log(a,a.toString(),a.valueOf())
//Number{...}, 1 ,[]
console.log(a+"12")//112 调用toString
console.log(a==0)//false 调用toString
console.log(a==1)//true 调用toString
console.log(a<1)//false 调用toString
console.log(a>0)//true 调用toString
console.log(String(a))//1 调用toString
console.log(Number(a))//1 调用toString
alert(a)//1 调用toString
confirm(a)//1 调用toString

valueOf偏向于运算,但我们从上面的结果可以看出,当重写valueOf返回对象时,则会调用toString

关于内置对象的toString和valueOf

var obj={},
date = new Date,
reg = new RegExp(/120/),
arr = [1,2,3,4],
str = new String("12"),
num = new Number(123),
boo = new Boolean(1)

console.log(obj.toString(),typeof obj.toString(),obj.valueOf(),typeof obj.valueOf())
//[object Object], string, {}, object
console.log(date.toString(),typeof date.toString(),date.valueOf(),typeof date.valueOf())
//Tue Sep 29 2020 15:19:44 GMT+0800 (中国标准时间), string, 1601363984183, number
console.log(reg.toString(),typeof reg.toString(),reg.valueOf(),typeof reg.valueOf())
// /120/, string ,/120/, object
console.log(arr.toString(),typeof arr.toString(),arr.valueOf(),typeof arr.valueOf())
//1,2,3,4 ,string (4) ,[1, 2, 3, 4] ,object
console.log(str.toString(),typeof str.toString(),str.valueOf(),typeof str.valueOf())
//12 ,string, 12 ,string
console.log(num.toString(),typeof num.toString(),num.valueOf(),typeof num.valueOf())
//123 ,string ,123, number
console.log(boo.toString(),typeof boo.toString(),boo.valueOf(),typeof boo.valueOf())
//true ,string, true ,boolean

特殊:

  • 数组的toString相当于调用了join(",")
  • Date的valueOf()返回时间戳
  • console.log()可能输出对象或字符串,在输出函数或者Date对象时,调用的是其原型上的toString

总结:toString和valueOf是对象的默认方法,toString返回的是对象的字符串、而valueOf是返回对象的原始值,使用操作符运算时默认调用valueOf方法,调用alert、confirm时默认调用toString,强制类型转化的String()和Number()则分别调用toString和valueOf;但是若重写两个方法,会先按照默认规则调用,但若重写的方法返回值为引用类型时,则会调用另一方法,当返回值也为引用类型时,则会报错。


此文章主要是来分享一下我自己发现的结论,而关于重写是否会提升两个方法之间的优先级,目前没有发现,也没找到比较详细的解释,所以若读者有不一样的发现或者认为我的结论有误,非常诚挚地欢迎你来一起探讨~