对象的valueof和toString

1,945 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

前言

这两个属性方法,其实我们天天经常接触,有显示的和隐性的。
数据类型经常用到toString

const toString = Object.prototype.toString
function getType(obj){
    return toString.call(obj).slice(8,-1).toLowerCase();
}

getType(null) // null
getType(undefined)  // undefined

上面方法null的获得的类型是 null, 可不等于typeof nullobject
更多获取数据类型的方法方式参见 判断数据类型几种方式, 里面提到大约8种方式,管饱。 但是仅仅限于这个作用吗。

valueOf 又是什么鬼,没直接用过,没事,今天一起来学习学习。

正文开始之前,一定要清楚一点,这两个方法都是原型上的方法。

Object.prototype.toString

返回一个表示该对象的字符串。

MDN文献:

默认情况下,toString() 方法被每个 Object 对象继承。如果此方法在自定义对象中未被覆盖,toString() 返回 "[object type]",其中 type 是对象的类型。

({}).toString() // [object Object]

特殊注意的一点就是, 虽然nullundefined没法直接调用,
但是可以作为Object.prototype.toString.call 或者Object.prototype.toString.apply调用的参数

Object.prototype.toString.call(null)  // [object Null]
Object.prototype.toString.apply(null) // [object Null]

文献中有提到,这只是此方法未被覆盖的时候,我们有时候为了特定情况,就会进行覆盖。

function Person(name, age){
    this.name = name;
    this.age = age;
}

Person.prototype.toString = function (){
   return "I'am " + this.name + ", " + this.age    
}

var person = new Person("tom", 18);
console.log(person + ""); //  I'am Tom, 18

Object.prototype.valueOf

返回指定对象的原始值。

** 对象**返回值
Array返回数组对象本身
Boolean布尔值
Date存储的时间是从 1970 年 1 月 1 日午夜开始计的毫秒数 UTC。
Function函数本身
Number数字值
Object对象本身。这是默认情况
String字符串

MDN关于valueOf的中文文献中:

截图_20215730085742.png

说Math和Error没有valueOf方法,你信吗,我不信啊。

Math.valueOf() // Math {abs: ƒ, acos: ƒ, acosh: ƒ, asin: ƒ, asinh: ƒ, …}
new Math() // Uncaught TypeError: Math is not a constructor

Error.valueOf() // ƒ Error() { [native code] }
(new Error()).valueOf() // Error at <anonymous>:1:2

所以嘛,时刻保持思考,实践是检查真相的唯一真理。

同样的,valueOf也是可以改写的。


function Person(name, age){
    this.name = name;
    this.age = age;
}

Person.prototype.valueOf = function (){
   return "I'am " + this.name + ", " + this.age    
}

var person = new Person("tom", 18);
console.log(person + ""); //  I'am Tom, 18

瞧瞧,你发现了什么?

转为原值的时候,是先走valueOf, 再走toString

稍微调整一下代码,再看个例子:


Person.prototype.valueOf = function (){
   return this.age    
}

var person = new Person("tom", 18);
console.log(person + 18); //  36

纳里!!!, 刚不说了吗,转原值,先先走valueOf, 再走toString, 这valueOf返回了原值,不就直接OK了吗

Symbol.toPrimitive

根据上面的例子,再进一步

Person.prototype[Symbol.toPrimitive] = function (){
   return "I'am " + this.name + ", " + this.age
}

Person.prototype.valueOf = function (){
   return this.age
}

var person = new Person("tom", 18);
console.log(person + 18); //  I'am Tom, 18

哇,再修改一下优先级

  1. Symbol.toPrimitive
  2. Oject.prototye.valueOf,
  3. Object.prototype.toString

这里,其有一个返回原值了,就over了。, 不然接着往下调用。

看起是是对的,其实也不对。

Person.prototype.valueOf = function (){
   return this.age
}

Person.prototype.toString = function (){
   return this.name
}


var person = new Person("tom", 18);
console.log(`${person}`); // tom

输出是tom, 并不是 18。

正确姿势:

  • Symbol.toPrimitive 优先
  • 如果预期转为字符串,Object.prototype.toString
  • 如果无预期转为字符串, 先走 Oject.prototye.valueOf, 再走Object.prototype.toString, 特例是Date类型,是先toString,再valueOf

验证一下特殊的Date属性:

Date.prototype.toString = function(){return 100}
Date.prototype.valueOf = function(){return 99}

new Date() == 99   // false
new Date() == 100 // true

我们把toString修改为返回一个对象, 这时候发现比较结果反了过来,valueOf生效了。

Date.prototype.toString = function(){return {}}
Date.prototype.valueOf = function(){return 99}

new Date() == 99   // true
new Date() == 100 // false

可以看出,先调用的是toString

最后巩固一下:

Person.prototype.valueOf = function (){
   return this
}

Person.prototype.toString = function (){
   return this.name
}
var person = new Person("tom", 18);
console.log(person + " 哈哈");

请问,输出的结果是什么呢?

小结

今天你收获了吗?