类型检测的疑惑

本想试试用js语言精粹上的例子区分数组对象,发现直接使用[].toString和使用Object.prototype.toString.apply不一样。

image.png
当使用a.toString()时结果是1,2,3
当使用Object.prototype.toString.apply(a)时结果是[object Array]

Object.prototype.toString 和 [].toString不是同一个?
是的
([]).toString === Object.prototype.toString;//false

([1,2,3]).toString()打印值的原理

image.png
通过上图看到Object.prototype.toString是toString最顶层最初始的方法,
image.png
通过上图可以看到除了Object类型的{},别的所有类型都没走到最顶层的Object的toString方法,而是中途被Object相应的子类重写了,走了相应的类型的打印方法。
所以上边的疑问Object.prototype.toString 和 [].toString不是同一个?是的,[].toString()中途被重写了。

Object.prototype.toString.apply([])类型检测的原理

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

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

若参数不为 nullundefined,则将参数转为对象,再作判断。
对于原始类型,转为对象的方法即装箱,此处不赘述。

转为对象后,取得该对象的 [Symbol.toStringTag] 属性值(可能会遍历原型链)作为 tag,如无该属性,或该属性值不为字符串类型,则依下表取得 tag, 然后返回 "[object " + tag + "]" 形式的字符串。

  1. // Boolean 类型,tag 为 "Boolean"
  2. Object.prototype.toString.call(true); // => "[object Boolean]"
  3. // Number 类型,tag 为 "Number"
  4. Object.prototype.toString.call(1); // => "[object Boolean]"
  5. // String 类型,tag 为 "String"
  6. Object.prototype.toString.call(""); // => "[object String]"
  7. // Array 类型,tag 为 "String"
  8. Object.prototype.toString.call([]); // => "[object Array]"
  9. // Arguments 类型,tag 为 "Arguments"
  10. Object.prototype.toString.call((function() {
  11. return arguments;
  12. })()); // => "[object Arguments]"
  13. // Function 类型, tag 为 "Function"
  14. Object.prototype.toString.call(function(){}); // => "[object Function]"
  15. // Error 类型(包含子类型),tag 为 "Error"
  16. Object.prototype.toString.call(new Error()); // => "[object Error]"
  17. // RegExp 类型,tag 为 "RegExp"
  18. Object.prototype.toString.call(/\d+/); // => "[object RegExp]"
  19. // Date 类型,tag 为 "Date"
  20. Object.prototype.toString.call(new Date()); // => "[object Date]"
  21. // 其他类型,tag 为 "Object"
  22. Object.prototype.toString.call(new class {}); // => "[object Object]"

总结

就是使用Object类最顶层的toString方法去判断数据类型,Object类的方法在prototype上,Object.proto原型是null,Object是最顶层类,其他类均继承它,并在不同程度上重写了它的方法,比如这个toString。

部署Symbol.toStringTag

下面为部署了 Symbol.toStringTag 的例子。可以看出,属性值期望是一个字符串,否则会被忽略。

var o1 = { [Symbol.toStringTag]: "A" };
var o2 = { [Symbol.toStringTag]: null };
Object.prototype.toString.call(o1);      // => "[object A]"
Object.prototype.toString.call(o2);      // => "[object Object]"

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

class A {}
A.prototype[Symbol.toStringTag] = "A";
Object.prototype.toString.call(new A());   // => "[object A]"

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


参考

作者:吃冬瓜群众代表
链接:https://zhuanlan.zhihu.com/p/118793721
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。