定義
數(shù)組(array)是按次序排列的一組值。每個值的位置都有編號(從0開始)侍匙,整個數(shù)組用方括號表示。
var arr = ['a', 'b', 'c'];
上面代碼中的a
叮雳、b
想暗、c
就構(gòu)成一個數(shù)組,兩端的方括號是數(shù)組的標(biāo)志债鸡。a
是0號位置江滨,b
是1號位置,c
是2號位置厌均。
除了在定義時賦值唬滑,數(shù)組也可以先定義后賦值。
var arr = [];
arr[0] = 'a';
arr[1] = 'b';
arr[2] = 'c';
任何類型的數(shù)據(jù)棺弊,都可以放入數(shù)組晶密。
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;}
上面數(shù)組arr
的3個成員依次是對象、數(shù)組模她、函數(shù)稻艰。
如果數(shù)組的元素還是數(shù)組,就形成了多維數(shù)組侈净。
var a = [[1, 2], [3, 4]];
a[0][1] // 2
a[1][1] // 4
數(shù)組的本質(zhì)
本質(zhì)上尊勿,數(shù)組屬于一種特殊的對象。typeof
運算符會返回數(shù)組的類型是object
畜侦。
typeof [1, 2, 3] // "object"
上面代碼表明元扔,typeof
運算符認(rèn)為數(shù)組的類型就是對象。
數(shù)組的特殊性體現(xiàn)在旋膳,它的鍵名是按次序排列的一組整數(shù)(0澎语,1,2…)。
var arr = ['a', 'b', 'c'];
Object.keys(arr)
// ["0", "1", "2"]
上面代碼中擅羞,Object.keys
方法返回數(shù)組的所有鍵名尸变。可以看到數(shù)組的鍵名就是整數(shù)0减俏、1召烂、2。
由于數(shù)組成員的鍵名是固定的(默認(rèn)總是0垄懂、1骑晶、2…),因此數(shù)組不用為每個元素指定鍵名草慧,而對象的每個成員都必須指定鍵名桶蛔。JavaScript 語言規(guī)定,對象的鍵名一律為字符串漫谷,所以仔雷,數(shù)組的鍵名其實也是字符串。之所以可以用數(shù)值讀取舔示,是因為非字符串的鍵名會被轉(zhuǎn)為字符串碟婆。
var arr = ['a', 'b', 'c'];
arr['0'] // 'a'
arr[0] // 'a'
上面代碼分別用數(shù)值和字符串作為鍵名,結(jié)果都能讀取數(shù)組惕稻。原因是數(shù)值鍵名被自動轉(zhuǎn)為了字符串竖共。
注意,這點在賦值時也成立俺祠。如果一個值總是先轉(zhuǎn)成字符串公给,再進(jìn)行賦值。
var a = [];
a[1.00] = 6;
a[1] // 6
上面代碼中蜘渣,由于1.00
轉(zhuǎn)成字符串是1
淌铐,所以通過數(shù)字鍵1
可以讀取值。
上一章說過蔫缸,對象有兩種讀取成員的方法:點結(jié)構(gòu)(object.key
)和方括號結(jié)構(gòu)(object[key]
)腿准。但是,對于數(shù)值的鍵名拾碌,不能使用點結(jié)構(gòu)吐葱。
var arr = [1, 2, 3];
arr.0 // SyntaxError
上面代碼中,arr.0
的寫法不合法校翔,因為單獨的數(shù)值不能作為標(biāo)識符(identifier)唇撬。所以,數(shù)組成員只能用方括號arr[0]
表示(方括號是運算符展融,可以接受數(shù)值)。
length 屬性
數(shù)組的length
屬性,返回數(shù)組的成員數(shù)量告希。
['a', 'b', 'c'].length // 3
JavaScript 使用一個32位整數(shù)扑浸,保存數(shù)組的元素個數(shù)。這意味著燕偶,數(shù)組成員最多只有 4294967295 個(232 - 1)個喝噪,也就是說length
屬性的最大值就是 4294967295。
只要是數(shù)組指么,就一定有length
屬性酝惧。該屬性是一個動態(tài)的值,等于鍵名中的最大整數(shù)加上1
伯诬。
var arr = ['a', 'b'];
arr.length // 2
arr[2] = 'c';
arr.length // 3
arr[9] = 'd';
arr.length // 10
arr[1000] = 'e';
arr.length // 1001
上面代碼表示晚唇,數(shù)組的數(shù)字鍵不需要連續(xù),length
屬性的值總是比最大的那個整數(shù)鍵大1
盗似。另外哩陕,這也表明數(shù)組是一種動態(tài)的數(shù)據(jù)結(jié)構(gòu),可以隨時增減數(shù)組的成員赫舒。
length
屬性是可寫的悍及。如果人為設(shè)置一個小于當(dāng)前成員個數(shù)的值,該數(shù)組的成員會自動減少到length
設(shè)置的值接癌。
var arr = [ 'a', 'b', 'c' ];
arr.length // 3
arr.length = 2;
arr // ["a", "b"]
上面代碼表示心赶,當(dāng)數(shù)組的length
屬性設(shè)為2(即最大的整數(shù)鍵只能是1)那么整數(shù)鍵2(值為c
)就已經(jīng)不在數(shù)組中了,被自動刪除了缺猛。
清空數(shù)組的一個有效方法缨叫,就是將length
屬性設(shè)為0。
var arr = [ 'a', 'b', 'c' ];
arr.length = 0;
arr // []
如果人為設(shè)置length
大于當(dāng)前元素個數(shù)枯夜,則數(shù)組的成員數(shù)量會增加到這個值弯汰,新增的位置都是空位。
var a = ['a'];
a.length = 3;
a[1] // undefined
上面代碼表示湖雹,當(dāng)length
屬性設(shè)為大于數(shù)組個數(shù)時咏闪,讀取新增的位置都會返回undefined
。
如果人為設(shè)置length
為不合法的值摔吏,JavaScript 會報錯鸽嫂。
// 設(shè)置負(fù)值
[].length = -1
// RangeError: Invalid array length
// 數(shù)組元素個數(shù)大于等于2的32次方
[].length = Math.pow(2, 32)
// RangeError: Invalid array length
// 設(shè)置字符串
[].length = 'abc'
// RangeError: Invalid array length
值得注意的是,由于數(shù)組本質(zhì)上是一種對象征讲,所以可以為數(shù)組添加屬性据某,但是這不影響length
屬性的值。
var a = [];
a['p'] = 'abc';
a.length // 0
a[2.1] = 'abc';
a.length // 0
上面代碼將數(shù)組的鍵分別設(shè)為字符串和小數(shù)诗箍,結(jié)果都不影響length
屬性癣籽。因為,length
屬性的值就是等于最大的數(shù)字鍵加1,而這個數(shù)組沒有整數(shù)鍵筷狼,所以length
屬性保持為0
瓶籽。
如果數(shù)組的鍵名是添加超出范圍的數(shù)值,該鍵名會自動轉(zhuǎn)為字符串埂材。
var arr = [];
arr[-1] = 'a';
arr[Math.pow(2, 32)] = 'b';
arr.length // 0
arr[-1] // "a"
arr[4294967296] // "b"
上面代碼中塑顺,我們?yōu)閿?shù)組arr
添加了兩個不合法的數(shù)字鍵,結(jié)果length
屬性沒有發(fā)生變化俏险。這些數(shù)字鍵都變成了字符串鍵名严拒。最后兩行之所以會取到值,是因為取鍵值時竖独,數(shù)字鍵名會默認(rèn)轉(zhuǎn)為字符串裤唠。
in 運算符
檢查某個鍵名是否存在的運算符in
,適用于對象预鬓,也適用于數(shù)組巧骚。
var arr = [ 'a', 'b', 'c' ];
2 in arr // true
'2' in arr // true
4 in arr // false
上面代碼表明,數(shù)組存在鍵名為2
的鍵格二。由于鍵名都是字符串劈彪,所以數(shù)值2
會自動轉(zhuǎn)成字符串。
注意顶猜,如果數(shù)組的某個位置是空位沧奴,in
運算符返回false
。
var arr = [];
arr[100] = 'a';
100 in arr // true
1 in arr // false
上面代碼中长窄,數(shù)組arr
只有一個成員arr[100]
滔吠,其他位置的鍵名都會返回false
。
for…in 循環(huán)和數(shù)組的遍歷
for...in
循環(huán)不僅可以遍歷對象挠日,也可以遍歷數(shù)組疮绷,畢竟數(shù)組只是一種特殊對象。
var a = [1, 2, 3];
for (var i in a) {
console.log(a[i]);
}
// 1
// 2
// 3
但是嚣潜,for...in
不僅會遍歷數(shù)組所有的數(shù)字鍵冬骚,還會遍歷非數(shù)字鍵。
var a = [1, 2, 3];
a.foo = true;
for (var key in a) {
console.log(key);
}
// 0
// 1
// 2
// foo
上面代碼在遍歷數(shù)組時懂算,也遍歷到了非整數(shù)鍵foo
只冻。所以,不推薦使用for...in
遍歷數(shù)組计技。
數(shù)組的遍歷可以考慮使用for
循環(huán)或while
循環(huán)喜德。
var a = [1, 2, 3];
// for循環(huán)
for(var i = 0; i < a.length; i++) {
console.log(a[i]);
}
// while循環(huán)
var i = 0;
while (i < a.length) {
console.log(a[i]);
i++;
}
var l = a.length;
while (l--) {
console.log(a[l]);
}
上面代碼是三種遍歷數(shù)組的寫法。最后一種寫法是逆向遍歷垮媒,即從最后一個元素向第一個元素遍歷舍悯。
數(shù)組的forEach
方法航棱,也可以用來遍歷數(shù)組,詳見《標(biāo)準(zhǔn)庫》的 Array 對象一章贱呐。
var colors = ['red', 'green', 'blue'];
colors.forEach(function (color) {
console.log(color);
});
// red
// green
// blue
數(shù)組的空位
當(dāng)數(shù)組的某個位置是空元素丧诺,即兩個逗號之間沒有任何值,我們稱該數(shù)組存在空位(hole)奄薇。
var a = [1, , 1];
a.length // 3
上面代碼表明,數(shù)組的空位不影響length
屬性抗愁。
需要注意的是馁蒂,如果最后一個元素后面有逗號,并不會產(chǎn)生空位蜘腌。也就是說沫屡,有沒有這個逗號,結(jié)果都是一樣的撮珠。
var a = [1, 2, 3,];
a.length // 3
a // [1, 2, 3]
上面代碼中沮脖,數(shù)組最后一個成員后面有一個逗號,這不影響length
屬性的值芯急,與沒有這個逗號時效果一樣勺届。
數(shù)組的空位是可以讀取的,返回undefined
娶耍。
var a = [, , ,];
a[1] // undefined
使用delete
命令刪除一個數(shù)組成員免姿,會形成空位,并且不會影響length
屬性榕酒。
var a = [1, 2, 3];
delete a[1];
a[1] // undefined
a.length // 3
上面代碼用delete
命令刪除了數(shù)組的第二個元素胚膊,這個位置就形成了空位,但是對length
屬性沒有影響想鹰。也就是說紊婉,length
屬性不過濾空位。所以辑舷,使用length
屬性進(jìn)行數(shù)組遍歷喻犁,一定要非常小心。
數(shù)組的某個位置是空位惩妇,與某個位置是undefined
株汉,是不一樣的。如果是空位歌殃,使用數(shù)組的forEach
方法乔妈、for...in
結(jié)構(gòu)、以及Object.keys
方法進(jìn)行遍歷氓皱,空位都會被跳過路召。
var a = [, , ,];
a.forEach(function (x, i) {
console.log(i + '. ' + x);
})
// 不產(chǎn)生任何輸出
for (var i in a) {
console.log(i);
}
// 不產(chǎn)生任何輸出
Object.keys(a)
// []
如果某個位置是undefined
勃刨,遍歷的時候就不會被跳過。
var a = [undefined, undefined, undefined];
a.forEach(function (x, i) {
console.log(i + '. ' + x);
});
// 0\. undefined
// 1\. undefined
// 2\. undefined
for (var i in a) {
console.log(i);
}
// 0
// 1
// 2
Object.keys(a)
// ['0', '1', '2']
這就是說股淡,空位就是數(shù)組沒有這個元素身隐,所以不會被遍歷到,而undefined
則表示數(shù)組有這個元素唯灵,值是undefined
贾铝,所以遍歷不會跳過。
類似數(shù)組的對象
如果一個對象的所有鍵名都是正整數(shù)或零埠帕,并且有length
屬性垢揩,那么這個對象就很像數(shù)組,語法上稱為“類似數(shù)組的對象”(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 function
上面代碼中叁巨,對象obj
就是一個類似數(shù)組的對象。但是呐籽,“類似數(shù)組的對象”并不是數(shù)組锋勺,因為它們不具備數(shù)組特有的方法。對象obj
沒有數(shù)組的push
方法狡蝶,使用該方法就會報錯庶橱。
“類似數(shù)組的對象”的根本特征,就是具有length
屬性牢酵。只要有length
屬性悬包,就可以認(rèn)為這個對象類似于數(shù)組。但是有一個問題馍乙,這種length
屬性不是動態(tài)值布近,不會隨著成員的變化而變化。
var obj = {
length: 0
};
obj[3] = 'd';
obj.length // 0
上面代碼為對象obj
添加了一個數(shù)字鍵丝格,但是length
屬性沒變撑瞧。這就說明了obj
不是數(shù)組。
典型的“類似數(shù)組的對象”是函數(shù)的arguments
對象显蝌,以及大多數(shù) 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
上面代碼包含三個例子曼尊,它們都不是數(shù)組(instanceof
運算符返回false
)酬诀,但是看上去都非常像數(shù)組。
數(shù)組的slice
方法可以將“類似數(shù)組的對象”變成真正的數(shù)組骆撇。
var arr = Array.prototype.slice.call(arrayLike);
除了轉(zhuǎn)為真正的數(shù)組瞒御,“類似數(shù)組的對象”還有一個辦法可以使用數(shù)組的方法,就是通過call()
把數(shù)組的方法放到對象上面神郊。
function print(value, index) {
console.log(index + ' : ' + value);
}
Array.prototype.forEach.call(arrayLike, print);
上面代碼中肴裙,arrayLike
代表一個類似數(shù)組的對象趾唱,本來是不可以使用數(shù)組的forEach()
方法的,但是通過call()
蜻懦,可以把forEach()
嫁接到arrayLike
上面調(diào)用甜癞。
下面的例子就是通過這種方法,在arguments
對象上面調(diào)用forEach
方法宛乃。
// forEach 方法
function logArgs() {
Array.prototype.forEach.call(arguments, function (elem, i) {
console.log(i + '. ' + elem);
});
}
// 等同于 for 循環(huán)
function logArgs() {
for (var i = 0; i < arguments.length; i++) {
console.log(i + '. ' + arguments[i]);
}
}
字符串也是類似數(shù)組的對象悠咱,所以也可以用Array.prototype.forEach.call
遍歷。
Array.prototype.forEach.call('abc', function (chr) {
console.log(chr);
});
// a
// b
// c
注意烤惊,這種方法比直接使用數(shù)組原生的forEach
要慢乔煞,所以最好還是先將“類似數(shù)組的對象”轉(zhuǎn)為真正的數(shù)組,然后再直接調(diào)用數(shù)組的forEach
方法柒室。
var arr = Array.prototype.slice.call('abc');
arr.forEach(function (chr) {
console.log(chr);
});
// a
// b
// c
參考鏈接
- Axel Rauschmayer, Arrays in JavaScript
- Axel Rauschmayer, JavaScript: sparse arrays vs. dense arrays
- Felix Bohm, What They Didn’t Tell You About ES5′s Array Extras
- Juriy Zaytsev, How ECMAScript 5 still does not allow to subclass an array