在JavaScript中,可以通过typeof
操作符来判断基本数据类型(Undefined、Null、Boolean、Number和String),同时相信大家也熟知typeof
对于对象的判断是不准确的,因为特殊值Null
被认为是一个空的对象的引用。
对于数组的创建,可以使用构造函数,通过传递数量
var colors = new Array(3);
也可以在构造函数中传递值来创建数组
var colors = new Array('red', 'blue', 'green');
创建数组的第二种基本方法是使用数组字面量
var colors = ['red', 'blue', 'green'];
判断一个对象是否为数组,最先想到的就是instanceof
操作符,通过判断对象是否为Array
的实例来达到目的
var array = [];
console.log(array instanceof Array); // true
使用instanceof
操作符的问题在于它假定只有一个全局执行环境。
然而这在某些特殊环境下并不安全,下面引用自MDN:
在浏览器中,我们的脚本可能需要在多个窗口之间进行交互。多个窗口意味着多个全局环境,不同的全局环境拥有不同的全局对象,从而拥有不同的内置类型构造函数。这可能会引发一些问题。比如,表达式
[] instanceof window.frames[0].Array
会返回false
,因为Array.prototype !== window.frames[0].Array.prototype
。
因此,需要另寻别的方法来判断。
大家知道在任何值上调用Object原生的toString()方法,都会返回一个[object NativeConstructorName]格式的字符串。
var colors = [];
console.log(Object.prototype.toString.call(colors)); // "[object Array]"
利用这一点我们可以进一步将其封装成一个通用函数
function isArray(value) {
return Object.prototype.toString.call(value).slice(8, -1) === 'Array';
}
在ES5中,为了解决这个问题,提供了Array.isArray()
方法来确定某个值到底是不是数组,而不管它是在哪个全局作用域中创建的。
var colors = [];
console.log(Array.isArray(colors)); // true
行文到此应该结束了,因为判断数组的方法其实没有什么好说的。但是在ES6中,由于增加了一种新数据类型Symbol
,并通过Symbol
暴露了一些内部操作,导致了在判断的结果上会出现一些不确定情况。
和本文相关的一个是Symbol.hasInstance
方法,它是执行操作符instanceof
时内部调用的方法,用于检测对象的继承信息。当我们调用colors instanceof Array
时,实际上调用的是Array[Symbol.hasInstance](colors)
。
如果没有了解过Symbol
的童鞋还不清楚这意味着什么,且看下面的例子:
var sameArray = {
[Symbol.hasInstance](instance) {
return Array.isArray(instance);
}
}
console.log([] instanceof sameArray); // true
这里我们定义了sameArray
的instance
行为,内部调用了Array.isArray()
方法来判断传入的参数是否为一个数组。同样我们可以改变一个class
的instanceof
行为,这里需要注意一点的是在class
中是作为类的静态方法。
class Person {
static [Symbol.hasInstance](instance) {
return Array.isArray(instance);
}
}
最重要的是我们可以直接改变内置Array的instanceof
行为,导致其完全不可靠。
var colors = [];
Object.defineProperty(Array, Symbol.hasInstance, {
value(v) {
return false;
}
})
console.log(colors instanceof Array); // false
console.log(Array.isArray(colors)); // true
上面我们提到使用Object原生的toString()
方法来判断值是否为数组的实例。这在ES5中是一个很有效的方式,但在ES6中我们同样可以通过Symbol.toStringTag
来改变Object.prototype.toString()
的默认值。
Array.prototype[Symbol.toStringTag] = 'Magic';
var colors = [];
console.log(Object.prototype.toString.call(colors)); // "[object Magic]"
这也意味着Object.prototype.toString()
不是一个十分可靠的识别对象类型的方式。
注意:虽然语言本身不会阻止你使用
Symbol.toStringTag
属性来改变对象内建的toString()
方法的默认值,但还是不建议这样做。
最后:不论是在ES5还是ES6中,最可靠和最安全的数组判断方法是使用原生的Array.isArray()
方法,而在ES3中我们可以直接使用Object.prototype.toString()
来达到数组识别的目的。