为什么要使用 Object.prototype.toString.call 这种写法?

  |   0 评论   |   0 浏览   |   Erioifpud

Object.prototype.toString.call 这种写法大家应该不陌生,这是检查对象类型的方式之一,那么为什么要以这种形式去使用呢?

我认为这个问题可以被拆解成三个小问题,分别是:

  1. 为什么要使用 Object.prototype.toString 来获取 toString
  2. 为什么要使用 Object 原型对象的 toString
  3. 为什么要使用 call 来调用函数?

为什么要使用 Object.prototype.toString 来获取 toString

为什么不能直接用 对象.toString 呢?这涉及到原型的概念,首先得清楚 Object原型对象提供了 toString 函数,那么只要原型链上存在 Object 原型对象(下文称为原始 toString),并且**链上的原型对象没有重写 toString**的话,用的就是原始 toString

DHWwwT.md.png

这里提到了一个重点概念 - 重写,实际上很多原型对象都会重写 toString 函数,比如 StringArray 等:

'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 的定义就不看了,描述如下:

  • 对于 nullundefined,抛出 TypeError
  • 对于 BooleanNumberString,创建一个相应类型的包装对象,将包装对象的值([[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) 的形式使用,需要通过 callapplybind 等函数将要检查对象以上下文的形式传递给他,如 toString.call(obj)

总结

  1. 使用 Object.prototype.toString 来取得原始的 toString 函数,避免原型重写或无原型
  2. 使用 call 将要检测的对象 obj 作为上下文传入

标题:为什么要使用 Object.prototype.toString.call 这种写法?
作者:Erioifpud
地址:https://blog.doiduoyi.com/articles/1607052417279.html

评论

发表评论