Object.prototype.toString.call(1); '[object Number]'
Object.prototype.toString.call(NaN); '[object Number]'
Object.prototype.toString.call('1'); '[object String]'
Object.prototype.toString.call(true); '[object Boolean]'
Object.prototype.toString.call(undefined); '[object Undefined]'
Object.prototype.toString.call(null); '[object Null]'
Object.prototype.toString.call(Symbol());'[object Symbol]'
Object.prototype.toString.call(foo); '[object Function]'
Object.prototype.toString.call([1,2,3]); '[object Array]'
Object.prototype.toString.call({});'[object Object]'
为什么要使用call
对于 Object.prototype.toString() 方法,会返回一个形如 “[object XXX]” 的字符串。
如果对象的 toString()方法未被重写,就会返回形如上面形式的字符串。
({}).toString(); // => "[object Object]"
Math.toString(); // => "[object Math]"
但是,大多数对象对于toString()方法都是重写了的,因此这时需要使用 call()方法来调用未被重写前的toString方法。
var x = {
toString() {
return "X";
},
};
x.toString(); // => "X"
Object.prototype.toString.call(x); // => "[object Object]"
参数问题
对于 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 + “]” 形式的字符串,而这个字符串便可以显示的告诉你数据是哪种类型,不会像typeof方法那样无法准确区分数组和对象,也不会像instanceof方法那样去猜测数据是哪种类型。
// Boolean 类型,tag 为 "Boolean"
Object.prototype.toString.call(true); // => "[object Boolean]"
// Number 类型,tag 为 "Number"
Object.prototype.toString.call(1); // => "[object Number]"
// 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]"
下面为部署了 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这种基本数据类型也正是因为在其原型链上部署了这个属性,因而能正确通过此方法进行正确数据类型判断)
console.log(Object.prototype.toString.call(Symbol())); //"[object Symbol]"
以上便是此方法的基本原理实现。综合以上,我更推荐大家使用这个方法去准确显示的检测数据类型。