为什么要使用 Object.prototype.toString.call 这种写法?
Object.prototype.toString.call
这种写法大家应该不陌生,这是检查对象类型的方式之一,那么为什么要以这种形式去使用呢?
我认为这个问题可以被拆解成三个小问题,分别是:
- 为什么要使用
Object.prototype.toString
来获取toString
? - 为什么要使用
Object
原型对象的toString
? - 为什么要使用
call
来调用函数?
为什么要使用 Object.prototype.toString
来获取 toString
?
为什么不能直接用 对象.toString
呢?这涉及到原型的概念,首先得清楚 Object
的原型对象提供了 toString
函数,那么只要原型链上存在 Object
原型对象(下文称为原始 toString
),并且**链上的原型对象没有重写 toString
**的话,用的就是原始 toString
。
这里提到了一个重点概念 - 重写,实际上很多原型对象都会重写 toString
函数,比如 String
、Array
等:
'abc'.toString() // abc
[1, 2].toString() // 1,2
'abc'.toString === Object.prototype.toString // false
[1, 2].toString === Object.prototype.toString // false
可以看到他们的 toString
并不是原始 toString
,都是重写后的。
接下来考虑另一种情况,任意对象的原型链上一定有 Object
原型对象吗?答案是否,你可以通过 Object.create(null)
创建出一个原型为空的对象:
const obj = Object.create(null)
obj.__proto__ // undefined
obj.toString // undefined
他本身是一个空对象,原型链也是空的,那么自然也就没有 toString
了。
既然直接取对象的 toString
会存在重写 toString
和没有 toString
的情况,所以使用 Object.prototype.toString
这种写法是最稳妥的,因为他就是原始 toString
,并且无法被覆写或者删除。
为什么要使用 Object
原型对象的 toString
?
任意对象的 toString
(如果存在)不能检查对象类型吗?不能,可以看一下 ECMAScript 中关于 Object.prototype.toString
的说明:
Object.prototype.toString()
When the toString method is called, the following steps are taken:
当 toString 方法被调用后,执行以下步骤:
1. If the this value is undefined, return "[object Undefined]".
1. 如果 this 的值为 undefined,返回 "[object Undefined]"。
2. If the this value is null, return "[object Null]".
2. 如果 this 的值为 null,返回 "[object Null]"
3. Let O be the result of calling ToObject passing the this value as the argument.
3. 将 this 传入 ToObject,并且用 O 表示调用后的结果。
4. Let class be the value of the [[Class]] internal property of O.
4. 将 O 的内部属性 [[Class]] 记作 class。
5. Return the String value that is the result of concatenating the three Strings "[object ", class, and "]".
5. 返回 "[object ",class, 和 "]" 拼接出的字符串。
ToObject
的定义就不看了,描述如下:
- 对于
null
和undefined
,抛出TypeError
。 - 对于
Boolean
、Number
和String
,创建一个相应类型的包装对象,将包装对象的值([[PrimitiveValue]])设置为传入的参数并返回。 - 对于
Object
,将参数原样返回。
简单来说就是将非空的基本类型数据装箱。
回到 toString
,可以从定义中看出 Object.prototype.toString
有返回 this
类型的功能,其他原型对象重写后就不一定会保留这个功能。
比如 Array
原型对象的 toString
,会优先将 this
通过 join
转换成字符串返回。而 String
原型对象的 toString
则要求 this
为字符串。
所以要判断类型就应该使用 Object.prototype.toString
而非其他的 toString
。
为什么要使用 call
来调用函数?
为什么不使用 Object.prototype.toString()
来调用函数呢?这个就是 JavaScript 的基本知识了,首先参考上面的原始 toString
规范,这个函数是无参数的,只涉及上下文 this
,当我们把他作为“检测对象类型”的工具使用时,他检测的实际上是上下文 this
的类型。
所以不能通过 toString(obj)
的形式使用,需要通过 call
、apply
、bind
等函数将要检查对象以上下文的形式传递给他,如 toString.call(obj)
。
总结
- 使用
Object.prototype.toString
来取得原始的toString
函数,避免原型重写或无原型。 - 使用
call
将要检测的对象obj
作为上下文传入。
标题:为什么要使用 Object.prototype.toString.call 这种写法?
作者:Erioifpud
地址:https://blog.doiduoyi.com/articles/1607052417279.html