數(shù)組對(duì)于一個(gè)編程語(yǔ)言而言可謂舉足輕重,當(dāng)然 JavaScript
也對(duì)其相當(dāng)重視,下面我就將自己接觸到的數(shù)組有關(guān)的屬性和方法記錄下來(lái)。此文章會(huì)不斷完善,以供自己或他人查看。
簡(jiǎn)單介紹
有兩種方式聲明一個(gè)數(shù)組,如下所示
var arrayOne = new Array();
var arrayTwo = [];
按理說(shuō),以上兩種方式聲明的數(shù)組功能都是相同的,但是不建議使用第一種方式,準(zhǔn)確的說(shuō),為了可讀性以及代碼的執(zhí)行速度,我們選擇第二種方式。
雖然第一種方式不建議使用,但為了給其面子還是來(lái)介紹一下它的使用方法。Array
有三個(gè)構(gòu)造方法。
-
默認(rèn)的無(wú)參構(gòu)造方法
var array = new Array();
這種方式聲明的數(shù)組默認(rèn)數(shù)組長(zhǎng)度為 0 。
-
帶有數(shù)組元素的構(gòu)造方法
var array = new Array(1, 2, 3, 4);
-
指定數(shù)組長(zhǎng)度的構(gòu)造方法
var array = new Array(5);
數(shù)組的長(zhǎng)度需要設(shè)定在 0 - 2^32 -1 之間的一個(gè)整數(shù), 如果不是, 將會(huì)拋出 RangeError
異常。在事先不知道數(shù)組長(zhǎng)度的情況下還是不要盲目使用這個(gè)方法。舉個(gè)例子來(lái)說(shuō),當(dāng)前我們指定數(shù)組的長(zhǎng)度為 5,如果我們放進(jìn)數(shù)組 10 個(gè)元素,數(shù)組的長(zhǎng)度會(huì)自動(dòng)增加到 10 。而如果我們只放進(jìn)數(shù)組 1 個(gè)元素,那么其它的 4 個(gè)位置不就虛位以待了嗎? 這在無(wú)形中也就浪費(fèi)了我們的內(nèi)存資源。
也許你已經(jīng)看到了,上面介紹 2,3 兩種 Array
構(gòu)造方法存在沖突,如果我們指定如下代碼,瀏覽器將會(huì)如何解釋我們的代碼呢?
var array = new Array(1);
其實(shí)以上代碼會(huì)被默認(rèn)解釋為一個(gè)長(zhǎng)度為 1 的空數(shù)組,如果你真的想將傳進(jìn)去的一個(gè)值作為數(shù)組中的元素,那么你只能將這個(gè)值當(dāng)做字符串傳進(jìn)去了。
var array = new Array("1");
當(dāng)然,我相信大部分人也不會(huì)用 new Array()
去聲明一個(gè)數(shù)組,那么自然也不會(huì)有這樣的問(wèn)題存在了。
在 JavaScript
中, 數(shù)組被當(dāng)做是一個(gè)對(duì)象,我們可以用以下代碼去檢測(cè)。
console.log(typeof(array)); // return object
既然如此,那么問(wèn)題來(lái)了。 對(duì)于一個(gè)已知變量,我們?cè)趺磁袛嗨遣皇菙?shù)組呢?顯然我們不可以利用 typeof
了。有三種方法可以鑒別,下面來(lái)一一介紹。
在
ECMAScript5
中有一個(gè)新方法Array.isArray()
,這個(gè)方法可以幫主我們鑒別變量是否為數(shù)組,如果是數(shù)組,返回true
, 否則返回false
。 但是這種方法存在一個(gè)問(wèn)題,那就是老的瀏覽器并不支持ECMAScript5
。-
為了解決上一個(gè)方法中出現(xiàn)的問(wèn)題,我們可以自己定義一個(gè)
isArray()
方法。function isArray(x) { return x.constructor .toString() .indexOf("Array") > -1; }
-
第三種方法需要借助
instanceof
關(guān)鍵詞,這個(gè)方法可以確定一個(gè)對(duì)象是否由某一個(gè)構(gòu)造函數(shù)所創(chuàng)建。array instanceof Array
在使用數(shù)組的時(shí)候需要尤其注意,因?yàn)閿?shù)組是通過(guò)下標(biāo)來(lái)操作元素的,比如 a[0],a[1] ...
。但是有些時(shí)候由于錯(cuò)誤的操作,極可能引發(fā)不可預(yù)知的問(wèn)題,就像下面這樣。
var person = [];
person["name"] = "hwaphon";
person["age"] = 20;
可見(jiàn),我們聲明 person
為一個(gè)數(shù)組,卻以操作對(duì)象的方式操作它,這個(gè)時(shí)候 JavaScript
會(huì)自動(dòng)將其重定位為一個(gè)對(duì)象,這也就意味著數(shù)組當(dāng)中的方法可能就失效了。比如說(shuō),這個(gè)時(shí)候調(diào)用 person.length
會(huì)返回 0 。但是值得注意的是,由于在重定位之前 person
是利用數(shù)組的構(gòu)造函數(shù)產(chǎn)生的,所以這個(gè)時(shí)候如果你利用 Array.isArray()
去檢測(cè) person
會(huì)發(fā)現(xiàn)仍然返回 true
。
方法
說(shuō)到數(shù)組,那么我們的本心就是利用數(shù)組來(lái)存儲(chǔ)一組數(shù)據(jù),那么也就面臨了問(wèn)題,我們?cè)撊绾未嫒?shù)據(jù)呢?
首先,用如下代碼聲明一個(gè)數(shù)組。
var array = [];
可以利用如下代碼一直往數(shù)組的末尾添加一個(gè)元素。
array[array.length] = element;
至于如何獲取數(shù)組中的值,可以直接指定數(shù)組的下標(biāo),比如說(shuō) a[0]
(如果這個(gè)元素尚沒(méi)有指定值,將會(huì)返回 undefined
)。
存取元素都解決了,還需要面對(duì)的一個(gè)問(wèn)題是如何刪除元素,或許我們可以通過(guò)下面這段代碼刪除最后一個(gè)元素(至于其它位置的元素,只需改變 i
的值即可)
for (var i = 0; i < array.length; i++) {
array[i] = array[i + 1];
}
恩,看上去是這么回事,而且最后一個(gè)元素會(huì)被賦值為 undefined
,非常完美??墒钱?dāng)調(diào)用 array.length
的時(shí)候發(fā)現(xiàn)數(shù)組的長(zhǎng)度并沒(méi)有改變,因?yàn)檫@樣我們只是將值給刪除了而已。沒(méi)關(guān)系,還有辦法,我們?cè)谥匦侣暶饕粋€(gè)數(shù)組,將當(dāng)前數(shù)組非 undefined
的值復(fù)制進(jìn)去就是了。哎,好像又需要寫(xiě)一個(gè)函數(shù)專(zhuān)門(mén)去做這件事了。體貼的 JavaScript
設(shè)計(jì)者自然也想到了這一點(diǎn),我們可以使用其內(nèi)置的方法去解決刪除的問(wèn)題,比如 pop(), shift(), splice()
。下面來(lái)探討一下這些方法的使用。
我們可以使用 push(), unshift()
方法在數(shù)組中插入元素,二者的區(qū)別在于 push()
方法會(huì)將元素插在數(shù)組的末尾,unshift()
方法將元素插在數(shù)組的開(kāi)頭。
array.unshift(0);
array.push(5);
使用 pop(), shift()
方法在數(shù)組中刪除元素,二者的區(qū)別在于 pop()
刪除數(shù)組的末尾元素,shift()
方法刪除數(shù)組的起始元素。
array.pop();
array.shift();
可見(jiàn),我們可以使用 push() 和 shift()
模仿隊(duì)列的工作方式。
還有一個(gè)比較強(qiáng)大的方法,那就是 splice()
方法,我們可以利用它刪除任何位置的任何長(zhǎng)度元素,也可以替換掉數(shù)組中的一部分元素。
array.splice(1,2);
以上代碼的意思是,從數(shù)組下標(biāo) 1 開(kāi)始,連續(xù)刪除兩個(gè)元素。
array.splice(1,0,5,6,7,8,9);
以上代碼的意思是從數(shù)組下標(biāo) 1 開(kāi)始,刪除 0 個(gè)元素,并且將 5,6,7,8,9 五個(gè)元素插入到數(shù)組當(dāng)中去,而且是插入在下標(biāo) 1 的后面。
-
數(shù)組復(fù)制
使用slice(start, end)
方法,可以完成對(duì)一個(gè)數(shù)組的復(fù)制,當(dāng)然可以選擇性復(fù)制。var fullCopy = array.slice(); var partCopy = array.slice(1,3);
上面的第一種方法,是將 array
這個(gè)數(shù)組中的所有元素復(fù)制下來(lái)。 第二種方法指定了起始和終止下標(biāo),所以復(fù)制的是 array
中數(shù)組下標(biāo) 1-3 的元素。
-
數(shù)組合并
var arrayOne = [0, 1, 2]; var number = 3; var arrayTwo = [4, 5, 6]; var resultArray = arrayOne.concat(number, arrayTwo);
concat()
中的參數(shù)可指定多個(gè),不過(guò)要注意先后順序。
- 迭代器函數(shù)
JavaScript
內(nèi)置了很多迭代器函數(shù),下面來(lái)一一介紹。
every(callback, thisArg)
方法會(huì)迭代數(shù)組中的每一個(gè)元素,直到有一個(gè)元素返回了 false
。這個(gè)方法有兩個(gè)參數(shù),第一個(gè)參數(shù)是回調(diào)函數(shù),而且該回調(diào)函數(shù)中還包含了三個(gè)參數(shù),分別是 currentValue, index, arrayObj
。第二個(gè)參數(shù) thisArg
是可選的,用于指定回調(diào)函數(shù)中的 this
指針指向何處,如果不指定,則 this
指針為 undefined
。這個(gè)方法有什么用呢?我們可以用于判斷一組數(shù)據(jù)中的所有元素是否滿(mǎn)足同一條件。比如說(shuō),我們用下面這個(gè)數(shù)組保存了 一部分人的年齡信息。
var personAge = [18,25,102,-6];
我們可以用 every()
函數(shù)判斷所有的值是否都為正數(shù),因?yàn)橐粋€(gè)人的年齡不可能為負(fù)數(shù)吧。
personAge.every(function(currentValue, index){
if (currentValue < 0) {
console.log("Index : " + index + ", value : " + currentValue);
return false;
}
return true;
});
與 every()
方法對(duì)應(yīng)的還有一個(gè)方法叫做 some()
,這二者的用法是一樣的。這二者的差別在于,every()
方法用于檢測(cè)一個(gè)數(shù)組中的元素是否全都滿(mǎn)足同一條件,一旦有一個(gè)元素不滿(mǎn)足條件,方法就會(huì)執(zhí)行結(jié)束。而 some()
方法用于檢測(cè)一個(gè)數(shù)組中是否有一個(gè)元素滿(mǎn)足指定條件,如果有一個(gè)元素滿(mǎn)足,也即回調(diào)函數(shù)返回了 true
, 那么some()
方法就會(huì)結(jié)束執(zhí)行。舉個(gè)例子,高中的時(shí)候每次考完試,總有老師會(huì)問(wèn),有沒(méi)有同學(xué)考到 90 分啊,當(dāng)然老師的目的并不是想準(zhǔn)確的知道班上有多少人考到 90 分,他只是想知道是否有人突破這一分?jǐn)?shù)線(xiàn),所以他想得到的答案為 是或否。我們可以用 some()
來(lái)模擬這一場(chǎng)景。
var studentGrade = [55,66,77,88,99];
studentGrade.some(function(currentValue, index) {
if (currentValue >= 90) {
console.log("I got " + currentValue + " points!");
return true;
}
return false;
})
一般情況下,我們會(huì)利用 for
循環(huán)打印整個(gè)數(shù)組的值,但是為了方便我們也可以使用 forEach()
方法。這個(gè)方法的使用和上述介紹的 some(), every()
相同。比如下面利用 forEach()
遍歷整個(gè)數(shù)組。
var numbers = [55,66,77,88,99];
numbers.forEach(function(currentVaule, index) {
console.log("Index : " + index + ", value : " + currentVaule);
});
下面介紹兩種能夠返回新數(shù)組的迭代器方法。filter(callback, thisArg)
可以根據(jù)條件返回一個(gè)新的數(shù)組。
var numbers = [55,66,77,88,99];
var evenNumbers = numbers.filter(function(currentValue, index) {
if (currentValue % 2 == 0) {
return true;
}
return false;
});
for (var i = 0; i < evenNumbers.length; i++) {
console.log(evenNumbers[i]);
}
上面打印的結(jié)果為 66,88
。 也就是說(shuō),filter()
方法會(huì)將滿(mǎn)足條件的值返回給新的數(shù)組。如果你想求一個(gè)數(shù)組中所有元素的和,那么 reduce(callback, thisArg)
方法能夠幫到你,不過(guò)值得注意的是,這個(gè)方法的 callback
方法相比于上述介紹的有些差距,它的callback
形如 function(previousValue, currentValue, index, arrayObj)
。下面利用這個(gè)函數(shù)來(lái)求一個(gè)數(shù)組的和。
var numbers = [55,66,77,88,99];
var result = numbers.reduce(function(previousValue, currentValue, index) {
return previousValue + currentValue;
});
console.log(result);
- 排序
可以利用 reverse()
方法將一個(gè)數(shù)組中的元素逆序排列。值得注意的是這里的逆序排列只是將數(shù)組中的元素按照初始時(shí)的反過(guò)來(lái)的排放而已,并沒(méi)有涉及到排序問(wèn)題。
var numbers = [1, 2, 3, 6, 5, 4, 11, 12, 13, 16, 15];
numbers.reverse();
for (var i = 0; i < numbers.length; i++) {
console.log(numbers[i]);
}
這段代碼的返回結(jié)果為 15,16,13,12,11,4,5,6,3,2,1
。
上面沒(méi)有涉及到排序,當(dāng)然我們可以利用 sort()
方法對(duì)數(shù)組進(jìn)行排序。
var numbers = [1, 2, 3, 6, 5, 4, 11, 12, 13, 16, 15];
numbers.sort();
for (var i = 0; i < numbers.length; i++) {
console.log(numbers[i]);
}
這段代碼的返回結(jié)果為 1,11,12,13,15,16,2,3,4,5,6
。 確定這是排序結(jié)果?天了嚕,這簡(jiǎn)直是在逗我。為什么會(huì)這樣呢? 因?yàn)?sort()
函數(shù)是將數(shù)組中的元素當(dāng)做字符串來(lái)排序的,字符串又是根據(jù)字符在 ASCII
碼來(lái)進(jìn)行排序的。所以,我們只能自己去定義排序規(guī)則。
var numbers = [1, 2, 3, 6, 5, 4, 11, 12, 13, 16, 15];
numbers.sort(function(first, second){
return first - second;
});
for (var i = 0; i < numbers.length; i++) {
console.log(numbers[i]);
}
你可以根據(jù)自己的需要,為 sort()
指定用于比較的函數(shù),此函數(shù)有三種類(lèi)型的返回值,即 正數(shù),0, 負(fù)數(shù)。
- 輸出為字符串
為了滿(mǎn)足某種需要,我們可能會(huì)將一個(gè)數(shù)組作為一個(gè)字符串返回,最簡(jiǎn)單的辦法是借助于 toString()
。
var numbers = [66,77,88,99];
var numberString = numbers.toString();
console.log(numberString);
執(zhí)行結(jié)果為 66,77,88,99
。 可見(jiàn),toString()
方法默認(rèn)將兩個(gè)數(shù)組元素用逗號(hào)隔開(kāi),當(dāng)然我們還可以使用 join()
改變這一默認(rèn)表現(xiàn)。
var numbers = [66,77,88,99];
var numberString = numbers.join("->");
console.log(numberString);
這時(shí)候的輸出結(jié)果為 66->77->88->99
。
ES6 新增
Array.from()
該方法可以將一些特定的對(duì)象轉(zhuǎn)換成數(shù)組對(duì)象,比如該對(duì)象具有 Iterator
接口或者該對(duì)象擁有 length
屬性。在 ES5
中,如果我們想把 arguments
轉(zhuǎn)換成一個(gè)數(shù)組,一般會(huì)使用以下語(yǔ)句。
[].slice.call(arguments);
Array.prototype.slice.call(arguments);
利用 Array.from()
可以簡(jiǎn)化上述過(guò)程。
Array.from(arguments);
這個(gè)函數(shù)一共有三個(gè)參數(shù),第二個(gè)參數(shù)的作用類(lèi)似于 map
, 即對(duì)每個(gè)位置的元素進(jìn)行處理,然后寫(xiě)入數(shù)組。第三個(gè)參數(shù)用于綁定 this
。
Array.of()
用于彌補(bǔ) Array
構(gòu)造函數(shù)的不足,因?yàn)閷?duì)于 new Array()
語(yǔ)句而言,如果只指定一個(gè)參數(shù)的話(huà),那一定是數(shù)組的長(zhǎng)度。比如下面這條語(yǔ)句用于創(chuàng)建一個(gè)長(zhǎng)度為 1 的數(shù)組。
var array = new Array(1);
而與此不同的是,Array.of()
無(wú)論傳入多少參數(shù),都會(huì)構(gòu)建一個(gè)數(shù)組,并把參數(shù)當(dāng)作元素傳入數(shù)組,比如下面這條語(yǔ)句就是創(chuàng)建一個(gè)長(zhǎng)度為 1 而且有一個(gè)值為 1 的元素的數(shù)組。
var array = Array.of(1);
在 ES5
中,可以用以下語(yǔ)句模擬 Array.of()
的功能。
function ArrayOf() {
return [].slice.call(arguments);
}
copyWithin()
使用這個(gè)方法可以將數(shù)組內(nèi)部指定位置的值復(fù)制到其他位置,這個(gè)變動(dòng)發(fā)生在數(shù)組內(nèi)部。此函數(shù)接受三個(gè)參數(shù),第一個(gè)參數(shù)是必須指定的,用于設(shè)置從哪個(gè)位置開(kāi)始替換數(shù)據(jù)。第二個(gè)參數(shù)是可選的,用于設(shè)置從哪個(gè)位置開(kāi)始讀取數(shù)據(jù),默認(rèn)為 0,如果為負(fù)數(shù),則實(shí)際值為當(dāng)前值加上數(shù)組長(zhǎng)度。第三個(gè)參數(shù)是可選的,用于設(shè)置在哪個(gè)位置終止讀取數(shù)據(jù),默認(rèn)值為數(shù)組長(zhǎng)度的值,如果是負(fù)數(shù),則實(shí)際值為當(dāng)前值加上數(shù)組長(zhǎng)度。下面來(lái)看個(gè)例子。
var array = [1, 2, 3, 4, 5];
array.copyWithin(0, 3, 5);
console.log(array);
以上操作的意思就是將位置處在 [3, 5) 的元素放在位置 0 及其以后的位置上。所以上面代碼的操作結(jié)果就是 [4, 5, 3, 4, 5]。
find() 和 findIndex()
從方法的名字中都可以猜出這兩個(gè)函數(shù)的用途,find()
用于查找數(shù)組中第一個(gè)滿(mǎn)足條件的元素值,這個(gè)函數(shù)像是 some()
和 filter()
的結(jié)合。findIndex()
用于返回?cái)?shù)組中第一個(gè)滿(mǎn)足條件的元素的下標(biāo)。
var array = [1, 2, 3, 4, 5];
// result = 4
var result = array.find(function(value, index) {
return value >= 4;
});
findIndex()
和 find()
用法一樣,不過(guò)是返回的下標(biāo)。值得一提的是 find()
和 findIndex()
都能識(shí)別 NaN
, 而對(duì)于 indexOf()
和 lastIndexOf()
則無(wú)法識(shí)別 NaN
。
fill()
這個(gè)方法一般用與初始化空數(shù)組,它可以將數(shù)組中的所有元素置為相同的一個(gè)值,比如聲明一個(gè)長(zhǎng)度為 5 的數(shù)組,并將所有的值都置為 0 。
new Array(5).fill(0);
entries(), keys(), values()
這三個(gè)方法用于遍歷數(shù)組,entries()
用于對(duì)鍵值對(duì)進(jìn)行遍歷,keys()
用于對(duì)鍵進(jìn)行遍歷, values()
用于對(duì)值進(jìn)行遍歷,下面是一個(gè)例子。
var array = ['a', 'b', 'c', 'd', 'e'];
for(let index of array.keys()) {
console.log(index); // 0 1 2 3 4 5
}
for (let value of array.values()) {
console.log(value); // a b c d e
}
for(let item of array.entries()) {
console.log(item); // [0, 'a'] [1, 'b'] ...
}
不過(guò)遺憾的是,在我的電腦上沒(méi)有一個(gè)瀏覽器支持 values()
方法,而 keys()
和 entries()
都是沒(méi)有問(wèn)題的。