JavaScript 的數(shù)組也是一個比較有意思的主題,雖然名為數(shù)組(Array),但是根據(jù)數(shù)組對象上的方法來看,更像是將很多東西混在在一起的結(jié)果。而傳統(tǒng)的程序設(shè)計語言如 C/Java 中,數(shù)組內(nèi)的元素需要具有相同的數(shù)據(jù)類型,而作為弱類型的 JavaScript,則沒有這個限制,事實上,JavaScript 的同一個數(shù)組中,可以有各種完全不同類型的元素。
| 方法 | 描述 |
|---|---|
| concat() | 連接兩個或更多的數(shù)組,并返回結(jié)果。 |
| join() | 把數(shù)組的所有元素放入一個字符串。元素通過指定的分隔符進(jìn)行分隔。 |
| pop() | 刪除并返回數(shù)組的最后一個元素。 |
| push() | 向數(shù)組的末尾添加一個或更多元素,并返回新的長度。 |
| reverse() | 顛倒數(shù)組中元素的順序。 |
| shift() | 刪除并返回數(shù)組的第一個元素。 |
| slice() | 從某個已有的數(shù)組返回選定的元素。 |
| sort() | 對數(shù)組的元素進(jìn)行排序。 |
| splice() | 刪除元素,并向數(shù)組添加新元素。 |
| unshift() | 向數(shù)組的開頭添加一個或更多元素,并返回新的長度。 |
| valueOf() | 返回數(shù)組對象的原始值。 |
可以看出,JavaScript 的數(shù)組對象比較復(fù)雜,包含有 pop,push 等類似與棧的操作,又有 slice, reverse,sort 這樣類似與列表的操作?;蛟S正因為如此,JavaScript 中的數(shù)組的功能非常強(qiáng)大。
數(shù)組包括一些屬性和方法,其最常用的屬性則為 length,length 表示數(shù)組的當(dāng)前長度,與其他語言不同的是,這個變量并非只讀屬性,比如:
var array = new Array(1, 2, 3, 4, 5);
print(array.length);
array.length = 3;
print(array.length);<span style="font-size: small;"><span style="font-size: 13px;">
</span></span>
運(yùn)行結(jié)果為:
5
3
1,2,3
注意到最后的 print 語句的結(jié)果是”1,2,3”,原因是對 length 屬性的修改會使得數(shù)組后邊的元素變得不可用(如果修改后的 length 比數(shù)組實際的長度小的話),所以可以通過設(shè)置 length 屬性來將數(shù)組元素裁減。
另一個與其他語言的數(shù)組不同的是,字符串也可以作為數(shù)組的下標(biāo)(事實上,在 JavaScript 的數(shù)組中,數(shù)字下標(biāo)最終會被解釋器轉(zhuǎn)化為字符串,也就是說,所謂的數(shù)字下標(biāo)只不過是看著像數(shù)字而實際上是字符的屬性名),比如:
var stack = new Array();
stack['first'] = 3.1415926;
stack['second'] = "okay then.";
stack['third'] = new Date();
for(var item in stack){
print(typeof stack[item]);
}
運(yùn)行結(jié)果為:
number
string
object
在這個例子里,還可以看到不同類型的數(shù)據(jù)是如何存儲在同一個數(shù)組中的,這么做有一定的好處,但是在某些場合則可能形成不便,比如我們在函數(shù)一章中討論過的 sum 函數(shù),sum 接受非顯式的參數(shù)列表,使用這個函數(shù),需要調(diào)用者必須為 sum 提供數(shù)字型的列表(當(dāng)然,字符串無法做 sum 操作)。如果是強(qiáng)類型語言,則對 sum 傳入字符串?dāng)?shù)組會被編譯程序認(rèn)為是非法的,而在 JavaScript 中,程序需要在運(yùn)行時才能偵測到這一錯誤。
數(shù)組有這樣幾種方式來創(chuàng)建:
var array = new Array();
var array = new Array(10);//長度
var array = new Array("apple", "borland", "cisco");
不過,運(yùn)用最多的為字面量方式來創(chuàng)建,如果第三章中的 JSON 那樣,我們完全可以這樣創(chuàng)建數(shù)組:
var array = [];
var array = ["one", "two", "three", "four"];
下面我們通過一些實際的小例子來說明數(shù)組的使用(主要方法的使用):
向數(shù)組中添加元素:
var array = [];
array.push(1);
array.push(2);
array.push(3);
array.push("four");
array.push("five");
array.push(3.1415926);
前面提到過,JavaScript 的數(shù)組有列表的性質(zhì),因此可以向其中 push 不同類型的元素,接上例:
var len = array.length;
for(var i = 0; i < len; i++){
print(typeof array[i]);
}
結(jié)果為:
number
number
number
string
string
number
彈出數(shù)組中的元素:
for(var i = 0; i < len; i++){
print(array.pop());
}
print(array.length);
運(yùn)行結(jié)果如下,注意最后一個 0 是指 array 的長度為 0,因為這時數(shù)組的內(nèi)容已經(jīng)全部彈出:
3.1415926
five
four
3
2
1
0
join,連接數(shù)組元素為一個字符串:
array = ["one", "two", "three", "four", "five"];
var str1 = array.join(",");
var str2 = array.join("|");
print(str1);
print(str2);
運(yùn)行結(jié)果如下:
one,two,three,four,five
one|two|three|four|five
連接多個數(shù)組為一個數(shù)組:
var another = ["this", "is", "another", "array"];
var another2 = ["yet", "another", "array"];
var bigArray = array.concat(another, another2);
結(jié)果為:
one,two,three,four,five,this,is,another,array,yet,another,array
從數(shù)組中取出一定數(shù)量的元素,不影響數(shù)組本身:
print(bigArray.slice(5,9));
結(jié)果為:
this,is,another,array
slice 方法的第一個參數(shù)為起始位置,第二個參數(shù)為終止位置,操作不影響數(shù)組本身。下面我們來看splice方法,雖然這兩個方法的拼寫非常相似,但是功用則完全不同,事實上,splice是一個相當(dāng)難用的方法:
bigArray.splice(5, 2);
bigArray.splice(5, 0, "very", "new", "item", "here");
第一行代碼表示,從 bigArray 數(shù)組中,從第 5 個元素起,刪除 2 個元素;而第二行代碼表示,從第 5 個元素起,刪除0個元素,并把隨后的所有參數(shù)插入到從第 5 個開始的位置,則操作結(jié)果為:
one,two,three,four,five,very,new,item,here,another,array,yet,another,array
我們再來討論下數(shù)組的排序,JavaScript 的數(shù)組的排序函數(shù) sort 將數(shù)組按字母順序排序,排序過程會影響源數(shù)組,比如:
var array = ["Cisio", "Borland", "Apple", "Dell"];
print(array);
array.sort();
print(array);
執(zhí)行結(jié)果為:
Cisio,Borland,Apple,Dell
Apple,Borland,Cisio,Dell
這種字母序的排序方式會造成一些非你所預(yù)期的小 bug,比如:
var array = [10, 23, 44, 58, 106, 235];
array.sort();
print(array);
得到的結(jié)果為:
10,106,23,235,44,58
可以看到,sort 不關(guān)注數(shù)組中的內(nèi)容是數(shù)字還是字母,它僅僅是按照字母的字典序來進(jìn)行排序,對于這種情況,JavaScript 提供了另一種途徑,通過給 sort 函數(shù)傳遞一個函數(shù)對象,按照這個函數(shù)提供的規(guī)則對數(shù)組進(jìn)行排序。
function sorter(a, b){
return a - b;
}
var array = [10, 23, 44, 58, 106, 235];
array.sort(sorter);
print(array);
函數(shù) sorter 接受兩個參數(shù),返回一個數(shù)值,如果這個值大于 0,則說明第一個參數(shù)大于第二個參數(shù),如果返回值為0,說明兩個參數(shù)相等,返回值小于 0,則第一個參數(shù)小于第二個參數(shù),sort 根據(jù)這個返回值來進(jìn)行最終的排序:
10,23,44,58,106,235
當(dāng)然,也可以簡寫成這樣:
array.sort(function(a, b){return a - b;});//正序
array.sort(function(a, b){return b - a;});//逆序
雖然令人費(fèi)解,但是 JavaScript 的數(shù)組對象上確實沒有一個叫做 delete 或者 remove 的方法,這就使得我們需要自己擴(kuò)展其數(shù)組對象。一般來說,我們可以擴(kuò)展 JavaScript 解釋器環(huán)境中內(nèi)置的對象,這種方式的好處在于,擴(kuò)展之后的對象可以適用于其后的任意場景,而不用每次都顯式的聲明。而這種做法的壞處在于,修改了內(nèi)置對象,則可能產(chǎn)生一些難以預(yù)料的錯誤,比如遍歷數(shù)組實例的時候,可能會產(chǎn)生令人費(fèi)解的異常。
數(shù)組中的每個元素都是一個對象,那么,我們可以使用 delete 來刪除元素嗎?來看看下邊這個小例子:
var array = ["one", "two","three","four"];
//數(shù)組中現(xiàn)在的內(nèi)容為:
//one,two,three,four
//array.length == 4
delete array[2];
然后,我們再來看看這個數(shù)組的內(nèi)容:
one, two, undefined, four
//array.length == 4
可以看到,delete 只是將數(shù)組 array 的第三個位置上的元素刪掉了,可是數(shù)組的長度沒有改變,顯然這個不是我們想要的結(jié)果,不過我們可以借助數(shù)組對象自身的 slice 方法來做到。一個比較好的實現(xiàn),是來自于 jQuery 的設(shè)計者 John Resig:
//Array Remove - By John Resig (MIT Licensed)
Array.prototype.remove = function(from, to) {
var rest = this.slice((to || from) + 1 || this.length);
this.length = from < 0 ? this.length + from : from;
return this.push.apply(this, rest);
};
這個函數(shù)擴(kuò)展了 JavaScript 的內(nèi)置對象 Array,這樣,我們以后的所有聲明的數(shù)組都會自動的擁有 remove 能力,我們來看看這個方法的用法:
var array = ["one", "two", "three", "four", "five", "six"];
print(array);
array.remove(0);//刪除第一個元素
print(array);
array.remove(-1);//刪除倒數(shù)第一個元素
print(array);
array.remove(0,2);//刪除數(shù)組中下標(biāo)為0-2的元素(3個)
print(array);
會得到這樣的結(jié)果:
one,two,three,four,five,six
two,three,four,five,six
two,three,four,five
five
也就是說,remove 接受兩個參數(shù),第一個參數(shù)為起始下標(biāo),第二個參數(shù)為結(jié)束下標(biāo),其中第二個參數(shù)可以忽略,這種情況下會刪除指定下標(biāo)的元素。當(dāng)然,不是每個人都希望影響整個原型鏈(原因在下一個小節(jié)里討論),因此可以考慮另一種方式:
//Array Remove - By John Resig (MIT Licensed)
Array.remove = function(array, from, to) {
var rest = array.slice((to || from) + 1 || array.length);
array.length = from < 0 ? array.length + from : from;
return array.push.apply(array, rest);
};
其操作方式與前者并無二致,但是不影響全局對象,代價是你需要顯式的傳遞需要操作的數(shù)組作為第一個參數(shù):
var array = ["one", "two", "three", "four", "five", "six"];
Array.remove(array, 0, 2);//刪除0, 1, 2三個元素
print(array);
這種方式,相當(dāng)于給JavaScript內(nèi)置的Array添加了一個靜態(tài)方法。
在對象與 JSON 這一章中,我們討論了 for…in 這種遍歷對象的方式,這種方式同樣適用于數(shù)組,比如:
var array = [1, 2, 3, 4];
for(var item in array){
print(array[item]);
}
將會打?。?/p>
1
2
3
4
但是這種方式并不總是有效,比如我們擴(kuò)展了內(nèi)置對象 Array,如下:
Array.prototype.useless = function(){}
然后重復(fù)執(zhí)行上邊的代碼,會得到這樣的輸出:
1
2
3
4
function(){}
設(shè)想這樣一種情況,如果你對數(shù)組的遍歷做sum操作,那么會得到一個莫名其妙的錯誤,畢竟函數(shù)對象不能做求和操作。幸運(yùn)的是,我們可以用另一種遍歷方式來取得正確的結(jié)果:
for(var i = 0, len = array.length; i < len;i++){
print(array[i]);
}
這種 for 循環(huán)如其他很多語言中的寫法一致,重要的是,它不會訪問哪些下標(biāo)不是數(shù)字的元素,如上例中的 function,這個 function 的下標(biāo)為 useless,是一個字符串。從這個例子我們可以看出,除非必要,盡量不要對全局對象進(jìn)行擴(kuò)展,因為對全局對象的擴(kuò)展會造成所有繼承鏈上都帶上“烙印”,而有時候這些烙印會成為滋生 bug 的溫床。