数组Array
1.定义
数组(array)是按次序排列的一组值。每个值的位置都有编号(从0开始),整个数组用方括号表示。
任何类型的数据,都可以放入数组。
var arr = [ {a: 1}, [1,2,3], function(){return true;} ]; arr[0]//Object {a:1} arr[1]//[1,2,3] arr[2]//function(){return true;}
2、数组的本质
本质上,数组属于一种特殊的对象。typeof 返回数组的类型是 object 。
数组的特殊性提现在,它的键名是按次序排列的一组整数 ( 0, 1, 2... ) 。
var arr = ['a', 'b', 'c']; Object.keys(arr)//["0", "1", "2"]
JavaScript语言规定,对象的键名一律为字符串,所以,数组的键名也是字符串。之所以可以用数值读取,是因为非字符串的键名会被转为字符串。
var a = []; a[1.00] = 6; a[1] //6
上面的代码中,由于 1.00 转换成字符串是 1,所以通过数字键 1 可以读取值。
3、length 属性
只要是数组,就一定有 length 属性。该属性是一个动态的值,等于键名中最大整数加 1 。
数组的数字键不需要连续。
length属性是可写的。意味着 增加 or 删除 数组整数键内容。清除数组的一个有效方法,就是将 length 属性设为 0 。
var arr = [ 'a', 'b', 'c' ]; arr.length = 0; arr // []
var a = ['a']; a.length = 3; a[1] // undefined
如果人为设置 length 为不合法的值,JavaScript 会报错。
值得注意的是,由于数组本质上是一种对象,所以可以为数组添加属性,但是这不影响 length 属性的值。
var a = []; a['p'] = 'abc'; a.length // 0 a[2.1] = 'abc'; a.length // 0
如果数组的键名是添加超出范围的数值,该键名会自动转为字符串。
var arr = []; arr[-1] = 'a'; arr[Math.pow(2, 32)] = 'b'; arr.length // 0 arr[-1] //"a" arr[4294967296] // "b"
上面代码中,我们为数组 arr 添加了两个不合法的数字键,结果 length 属性没有发生变化。这些数字键都变成了字符串键名。最后两行之所以会取到值,是因为取键值时,数字键名会默认转为字符串。
4、in 运算符
检查某个键名是否存在的运算符 in ,适用于对象,也适用于数组。
注意,如果数组的某个位置是空位,in 运算符返回 false 。
var arr = []; arr[100] = 'a'; 100 in arr //true 1 in arr /false
上面代码中,数组 arr 只有一个成员 arr[100], 其他位置的键名都会返回 false 。
5、for...in 循环和数组的遍历
for...in 循环不仅可以遍历对象,也可以遍历数组,毕竟数组只是一种特殊的对象。但是,for...in 不仅会遍历数组所有的数字键,还会遍历非数字键。
var a = [1, 2, 3]; a.foo = true; for(var key in a){ console.log(key); } //0 //1 //2 //foo
上面代码在遍历数组时,也遍历到了非整数键 foo 。所以,不推荐使用 for...in 遍历数组
数组遍历可以考虑使用 for 循环或 while 循环。
var a = [1, 2, 3]; //for 循环 for(var i=0; i< a.length; i++){ console.log(a[i]); } //while循环 var i = 0; while(i < a.length){ console.log(a[i]); i++; } var l = a.length; while (i--){ console.log(a[i]); }
6、数组的空位
当数组的某个位置是空元素,即两个逗号之间没有任何值,我们称该数组存在空位(hole)。
var a = [1, , 1]; a.length //3
上面代码表明,数组的空位不影响 length 属性。
需要注意的是,如果最后一个元素后面有逗号,并不会产生空位。也就是说,有没有这个逗号,结果是一样的。
数组的空位可以读取,返回 undefined 。
使用 delete 命令删除一个数组成员,会形成空位,并且不会影响 length 属性。
var a = [1, 2, 3]; delete a[1]; a[1]//undefined a.length //3
上面代码用 delete 命令删除了数组的第二个元素,这个位置就形成了空位,但是对 lenght 属性没有影响。也就是说,length 属性不过滤空位。所以,使用 length 属性进行数组遍历,一定要非常小心。
数组的某个位置是空位,与某个位置是 undefined ,是不一样的。如果是空位,使用数组的 forEach 方法、for...in结构、以及 Object.keys 方法进行遍历,空位都会被跳过。如果某个位置是 undefined ,遍历的时候就不会被跳过。
7、类似数组的对象
如果一个对象的所有键名都是正整数或零,并且有 length 属性,那么这个对象都很像数组,语法上称为“类似数组的对象”(array-like object)。
var obj = { 0: 'a', 1: 'b', 2: 'c', length: 3 }; obj[0] // 'a' obj[1] // 'b' obj.length //3 obj.push('d') // TypeError: obj.push is not a fucntion
上面代码中,对象 obj 就是一个类似数组的对象。但是,“类似数组的对象”并不是数组,因为他们不具备数组特有的方法。对象 obj 没有数组的 push 方法,使用该方法就会报错。
“类似数组的对象”的根本特征,就是具有 length 属性。只要有 length 属性,就可以认为这个对象类似于数组。但是有一个问题,这种 length 属性不是动态值,不会随着成员的变化而变化。
var obj = { length:0 }; obj[3] = 'd'; obj.length //0
上面代码为对象 obj 添加了一个数字键,但是 length 属性没变。这就说明了 obj 不是数组。
典型的“类似数组的对象”是函数 arguments 对象,以及大多数 DOM元素集,还有字符串。
//arguments对象 function args(){ return arguments } var arrayLike = args('a', 'b'); arrayLike[0] // 'a' arrayLike.length // 2 arrayLike instanceof Array // false //DOM元素集 var elts = document.getElementsByTagName('h3'); elts.length //3 elts instanceof Array // false //字符串 'abc'[1] //'b' 'abc'.length //3 'abc' instanceof Array //false
上面代码包含三个例子,它们都不是数组(instanceof 运算符返回 false),但是看上去都非常像数组。
数组的 slice 方法可以将“类似数组的对象”变成真正的数组。
var arr = Array.prototype.slice.call(arrayLike);
除了转为真正的数组,“类似数组的对象”还有一个办法可以使用数组的方法,就是通过 call() 把数组的方法放到对象上面。
function print(value, index){ console.log(index + ' : ' + value); } Array.prototype.forEach.call(arrayLike, print);
上面代码中,arrayLike 代表一个类似数组的对象,本来是不可以使用数组的 forEach() 方法的,但是通过 call() ,可以把 forEach() 嫁接到 arrayLIke 上面调用。
下面的例子就是通过这种方法,在 arguments 对象上面调用 forEach 方法。
//forEach 方法 function logArgs(){ Array.prototype.forEach.call(arguments, function(elem, i){ console.log(i + '. ' + elem); }); } //等同于 for 循环 function logArgs(){ for(var i = 0; i<arguments.length; i++){ console.log(i + '. ' + arguments[i]); } }
字符串也是类似数组的对象,所以也可以用Array.prototype.forEach.call 遍历。
Array.prototype.forEach.call('abc', function(chr){ console.log(chr); }); //a //b //c
注意,这种方法比自己使用数组原生 forEach 要慢,所以最好还是先将“类似数组的对象”转为真正的数组,然后再直接调用数组的 forEach 方法。
var arr = Array.prototype.slice.call('abc'); arr.forEach(function (chr){ console.log(chr); }); //a //b //c
摘自:https://wangdoc.com/javascript/types/array.html