1. Object.prototype.toString.call()
该方法可以很好地区分各种类型,甚至是 null 和 undefined , 除了自定义对象类型(这个可用instanceof区分)。
console.log(Object.prototype.toString.call("jerry"));//[object String]
console.log(Object.prototype.toString.call(12));//[object Number]
console.log(Object.prototype.toString.call(true));//[object Boolean]
console.log(Object.prototype.toString.call(undefined));//[object Undefined]
console.log(Object.prototype.toString.call(null));//[object Null]
console.log(Object.prototype.toString.call({name: "jerry"}));//[object Object]
console.log(Object.prototype.toString.call(function(){}));//[object Function]
console.log(Object.prototype.toString.call([]));//[object Array]
console.log(Object.prototype.toString.call(new Date));//[object Date]
console.log(Object.prototype.toString.call(/\d/));//[object RegExp]
function Person(){};
console.log(Object.prototype.toString.call(new Person));//[object Object]
那为什么直接用obj.toString()就不行呢?(obj为要判断其类型的对象)
Answer:因为 toString 为Object的原型方法,而Array、function等具体类型作为Object的实例,都重写了toString方法。不同的对象类型调用toString方法时,根据原型链相关知识,调用的是对应的重写之后的toString方法(function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串.....),而不会去调用Object上原型toString方法(返回对象的具体类型),所以采用obj.toString()不能得到其obj所属的类型。
验证:将数组的toString方法删除,看看会是什么结果.
const arr=['hello','ki'];
console.log(Array.prototype.hasOwnProperty('toString')); // true
console.log(arr.toString()); // "hello,ki"
delete Array.prototype.toString; //从Array.prototype对象上删除toString属性
console.log(Array.prototype.hasOwnProperty('toString')); // false
console.log(arr.toString()); // [object Array]
console.log(Object.prototype.toString.call(arr)); // [object Array]
如上段代码所示,删除了Array原型上的 toString 方法后,再使用arr.toString()时,不再屏蔽Object原型对象上的toString方法,因此,返回了和Object.prototype.toString.call(arr)相同的结果。一图胜千言,还没明白的请看下图。(这里是为了验证,才从Array.prototype对象上删除toString属性,一般并不建议修改Array等内置对象)
Note: 该方法多用于判断内置对象,即非自定义对象类型。
2. instanceof : 通过判断对象的原型链中是不是能找到类型的 prototype。
例如使用 instanceof判断一个对象是否为数组,instanceof 会判断这个对象的原型链上是否会找到对应的 Array 的原型,找到返回 true,否则返回 false。
let Person=function(){};
let p=new Person();
console.log(1 instanceof Number); // false
console.log('string' instanceof String); // false
console.log(new Boolean(false) instanceof Boolean); // true
console.log(p instanceof Person); // true
但 instanceof 只能用来判断对象类型(很适合判断自定义对象类型),原始类型不可以。并且由于所有对象都继承自Object,所以任意对象 instanceof Object 都是 true。
3. Array.isArray():用来判断对象是否为数组(ES5新增的方法).当检测Array实例时,Array.isArray 优于 instanceof ,因为 Array.isArray 可以检测出 iframes.
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
xArray = window.frames[window.frames.length-1].Array;
var arr = new xArray(1,2,3); // [1,2,3]
// Correctly checking for Array
Array.isArray(arr); // true
Object.prototype.toString.call(arr); // true
// Considered harmful, because doesn't work though iframes
arr instanceof Array; // false
4. constructor:和instanceof原理差不多。需要事先知道待判断的对象大概属于何种类型,所以该方法更准确地说,是用来验证的。如果我们事先根本不知道一个对象实例的出处,那就不好使了。
let arr=[1,2,4];
alert(arr.constructor===Array); // true
总结:各个方法之间存在一定的性能差异,但网上说法并不统一,可能与浏览器也相关。另外文中的解释部分需要原型链的知识基础。