参考链接:
# 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来判断数据类型。返回值更精确:
为什么用了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]"
一开始仅认为toString是Object原型上的方法,只能通过Object来调用,
call、apply作用仅改变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事例:
关于[Symbol.toStringTag]
在我看来,[Symbol.toStringTag]更类似于一个对象的属性,一个通过中括号来获取值的属性:
Symbol.toStringTag也可以部署在原型链上:
新标准引入了
[Symbol.toStringTag]属性,是为了把此方法接口化,用于规范新引入的对象对此方法的调用。但对于“老旧”的对象,就只能直接输出值,以保证兼容性。
相对于【对象的属性】这种似是而非的话,[Symbol.toStringTag]更像一个所有对象隐藏的公共方法,一个暴露出来的接口