关于Object.prototype.toString.call()

1,770 阅读3分钟

参考链接:
# MDN-Object.prototype.toString()
# 从深入到通俗:Object.prototype.toString.call()
# 直接通过{}.toString这样调用,能执行吗?

知乎看到一篇文章:# Lodash 源码中的那些迷人的细节,看到第二点的时候,去补习了一下关于数据类型、以及数据类型的判断方面的知识,然后发现,关于Object.prototype.toString.call(),一直只是知道能判断数据类型,但是不知道其原理。于是此次深究了一下。

一、MDN-toString()

探究一个方法,第一步应该是要去一个相对权威的网站查看方法、返回值、学习如何使用,故我先去MDN搜索toString方法:
toString默认返回一个字符串,该字符串可以表示这个对象。
如何表示呢?通过[object type]的方式表示,其中,type 表示类型。

toString可以被重写,按照自己预想的模式输出,具体可以参照MDN:覆盖默认toString
另外,MDN中还提到可以用call、apply来判断数据类型。返回值更精确:

image.png

为什么用了call或者apply之后,返回值会发生变化,为什么会有更加精确的值?MDN-toString似乎并没有回答我这个问题。

二、深入toString()

2.1 为什么使用call、apply

在网络搜索之后,发现这篇文章:从深入到通俗:Object.prototype.toString.call()
文中提到:

如果对象的 toString() 方法未被重写,就会返回如上面形式的字符串。

({}).toString();     // => "[object Object]"
Math.toString();     // => "[object Math]"

但是,大多数对象,toString() 方法都是重写了的,这时,需要用 call() 或 Reflect.apply() 等方法来调用。

var x = {
  toString() {
    return "X";
  },
};
x.toString();                                     // => "X"
Object.prototype.toString.call(x);                // => "[object Object]"
Reflect.apply(Object.prototype.toString, x, []);  // => "[object Object]"

一开始仅认为toStringObject原型上的方法,只能通过Object来调用,
callapply作用仅改变this指向,更严谨。
至此,才得知,更是为了防止方法被重写

2.2 toString()原理

原理在文章顶部第二个参考文章中写的很好,通俗易懂,简明大方,这里我再写一遍以便自我理解,加深印象:

2.2.1 undefined、null的toString()

对于 Object.prototype.toString.call(arg),若参数arg为 null 或 undefined,直接返回结果。

Object.prototype.toString.call(null);       // => "[object Null]"
Object.prototype.toString.call(undefined);  // => "[object Undefined]"

2.2.2 undefined、null之外的数据类型的toString()

参考链接:
# javascript数据类型及装箱拆箱介绍
# MDN-Symbol.toStringTag
# Symbol的内置符号感觉看了好几遍还是很懵,有必要深究吗?

如果是原始数据类型,那么首先是封箱,封箱成一个对象
如果是引用数据类型,即对象,直接进行操作
然后寻找该对象的[Symbol.toStringTag]属性的值(可能也会按照原型链遍历),以此值为tag
返回一个字符串:[Object tag](其中,仅tag可变)
如下图JSON事例:

image.png

关于[Symbol.toStringTag]

在我看来,[Symbol.toStringTag]更类似于一个对象的属性,一个通过中括号来获取值的属性:

image.png

Symbol.toStringTag 也可以部署在原型链上:

image.png

新标准引入了 [Symbol.toStringTag] 属性,是为了把此方法接口化,用于规范新引入的对象对此方法的调用。但对于“老旧”的对象,就只能直接输出值,以保证兼容性。

相对于【对象的属性】这种似是而非的话,[Symbol.toStringTag]更像一个所有对象隐藏的公共方法,一个暴露出来的接口