类型检测的疑惑
本想试试用js语言精粹上的例子区分数组对象,发现直接使用[].toString和使用Object.prototype.toString.apply不一样。
当使用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()打印值的原理
通过上图看到Object.prototype.toString是toString最顶层最初始的方法,
通过上图可以看到除了Object类型的{},别的所有类型都没走到最顶层的Object的toString方法,而是中途被Object相应的子类重写了,走了相应的类型的打印方法。
所以上边的疑问Object.prototype.toString 和 [].toString不是同一个?是的,[].toString()中途被重写了。
Object.prototype.toString.apply([])类型检测的原理
对于 Object.prototype.toString.call(arg)
,若参数为 null
或 undefined
,直接返回结果。
Object.prototype.toString.call(null); // => "[object Null]"
Object.prototype.toString.call(undefined); // => "[object Undefined]"
若参数不为 null
或 undefined
,则将参数转为对象,再作判断。
对于原始类型,转为对象的方法即装箱,此处不赘述。
转为对象后,取得该对象的 [Symbol.toStringTag]
属性值(可能会遍历原型链)作为 tag
,如无该属性,或该属性值不为字符串类型,则依下表取得 tag
, 然后返回 "[object " + tag + "]"
形式的字符串。
// Boolean 类型,tag 为 "Boolean"
Object.prototype.toString.call(true); // => "[object Boolean]"
// Number 类型,tag 为 "Number"
Object.prototype.toString.call(1); // => "[object Boolean]"
// String 类型,tag 为 "String"
Object.prototype.toString.call(""); // => "[object String]"
// Array 类型,tag 为 "String"
Object.prototype.toString.call([]); // => "[object Array]"
// Arguments 类型,tag 为 "Arguments"
Object.prototype.toString.call((function() {
return arguments;
})()); // => "[object Arguments]"
// Function 类型, tag 为 "Function"
Object.prototype.toString.call(function(){}); // => "[object Function]"
// Error 类型(包含子类型),tag 为 "Error"
Object.prototype.toString.call(new Error()); // => "[object Error]"
// RegExp 类型,tag 为 "RegExp"
Object.prototype.toString.call(/\d+/); // => "[object RegExp]"
// Date 类型,tag 为 "Date"
Object.prototype.toString.call(new Date()); // => "[object Date]"
// 其他类型,tag 为 "Object"
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
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。