有以下 3 个判断数组的方法,请分别介绍它们之间的区别和优劣
Object.prototype.toString.call() 、 instanceof 、 Array.isArray()
Object.prototype.toString.call()
是原型上提供的判断数据类型的方法。
-
优点
可以判断引用类型和基本类型
Object.prototype.toString.call({0:1}) // "[object Object]" Object.prototype.toString.call([0,1]) // "[object Array]" Object.prototype.toString.call('1') // "[object String]"
-
缺点
无法区分出自定义对象。比如:
class A {}; class B{}; let a = new A(); let b = new B(); Object.prototype.toString(a); // "[object Object]" Object.prototype.toString(b); // "[object Object]"
instanceof
通过判断对象的原型链中是不是能找到类型prototype属性。
// __proto__: 代表原型对象链
instance.[__proto__...] === instance.constructor.prototype
// return true
- 优点
可以判断自定义对象,
可以判断数组a instanceof B // false a instanceof A // true
// instanceof可以区分数组是数组 [0,1] instanceof Array // true {0:1} instanceof Array // false
- 缺点
只能用来判断对象类型,无法判断原始类型。比如:
所有对象类型 instanceof Object 都是 true。比如:'1' instanceof String // false new String('1') instanceof String // true
[] instanceof Object // true new String('1') instanceof Object // true
Array.isArray()
用来判断对象是否为数组
// 判断是否是数组
Array.isArray([]); // true
Array.isArray({}); // false
- 与 instanceof比较
Array.isArray 优于 instanceof,前者可以检验出原始类型 - 与 Object.prototype.toString.call()比较
Array.isArray()是ES5新增的方法,当不存在 Array.isArray() ,可以用 Object.prototype.toString.call() 实现。if (!Array.isArray) { Array.isArray = function(arg) { return Object.prototype.toString.call(arg) === '[object Array]'; }; }
FAQ
- 问题一: Object.prototype.toString方法被调用时,都做了哪些事情?
1. 前置判断 - 如果this的值为undefined,则返回"[object Undefined]" - 如果this的值为null,则返回"[object Null]" 2. 获取this对象的[[Class]]属性的值 3. 计算,返回结果 "[object ", [[Class]]属性值, 以及 "]"
toString() 方法与 Object.prototype.toString.call()比较
toString() 是将数据转变成字符串。
var num = 123; num.toString(); // '123'
var obj = {}; obj.toString() // "[object Object]"
FAQ
-
问题一:原始类型没有属性和方法为什么可以调用toString()方法?
系统内部会经过
包装类
进行包装,相当于new Number(num).toString();包装类: new Number(); new String(); new Boolean() ;
-
问题二:包装类也都是对象,为什么调用toString()返回结果不一样?
原型链终端的
Object.prototype
对象上的toString()方法确实可以被继承下来,并且可以判断数据类型,
但不能满足所有的需求,所以子代的包装类
和数组
就重写了自己的toString()方法,
因此,当我们调用toString()方法时,实际上调用的自身原型
上重写的toString()方法。 -
问题三:为什么判断数据类型时使用Object.prototype.toString.call(xx)而不使用xx.toString?
xx.toString方法有可能会被改写。不可靠。
补充
typeof的特点
typeof 无法区分出对象和数组
typeof({0:1}) // object
typeof([0,1]) // object
instanceof原理
function instance_of(L, R) { // L 表示左表达式,R 表示右表达式
var O = R.prototype; // 取 R 的显示原型 即:R提供给子级的对象
L = L.__proto__; // 取 L 的隐式原型 即:L接收的父级的对象
while (true) {
if (L === null) return false;
if (O === L) return true; // 这里重点:R提供给子级的对象 = L接收的父级的对象时,则我们认为两者相等。
L = L.__proto__;
}
}
另附一个全面的判断方法
let class2type = {}
'Array Date RegExp Object Error'.split(' ').forEach(e => {
console.log(e, '[object ' + e + ']', e.toLowerCase(),);
class2type[ '[object ' + e + ']' ] = e.toLowerCase()
})
console.log('class2type', class2type);
function type(obj) {
if (obj == null) return String(obj)
console.log(obj);
return typeof obj === 'object' ?
class2type[ Object.prototype.toString.call(obj) ] || 'object' : typeof obj
}