數(shù)組常用操作方法總結(jié)
在 JavaScript 中,對于數(shù)組的操作非常頻繁,對應(yīng)的 API 也很豐富 。ECMAScript 規(guī)范在每一版發(fā)布時,都會提供新的 API 來增強數(shù)組的操作能力,下面將詳細介紹這些 API 的一些特性。
ES5 新增的 9 個API
1、forEach( callback[,thisArg] )
在 ES5 之前,我們可以通過 for 和 for in 兩種方式來遍歷數(shù)組,ES5 引入了一個新方法 forEach,使數(shù)組遍歷更加簡潔,forEach 需要傳遞兩個參數(shù),第一個參數(shù)是回調(diào)函數(shù),是必選參數(shù),第二個參數(shù)是一個對象,用來改變 callback 中的 this 指向,是可選參數(shù)。
var arr = ['a', 'b', 'c'];
arr.forEach(function(v, i, r) {
console.log(v, i, r);
})
>
a 0 ['a', 'b', 'c']
b 1 ['a', 'b', 'c']
c 2 ['a', 'b', 'c']
callback 中傳入了3個參數(shù) v,i,r 分別表示當(dāng)前元素、當(dāng)前位置、數(shù)組對象。再看看使用 thisArg 的例子:
var obj = {
print: function (a, b) {
console.log(a, b);
}
};
var arr = ['a', 'b', 'c'];
arr.forEach(function (v, i, a) {
this.print(v, i);
}, obj);
不傳 thisArgs 時,callback 中的 this 默認指向 window 對象,當(dāng)傳遞 thisArg 時,callback 中的 this 就指向了 thisArg,因此這個參數(shù)的目的就是為了改變回調(diào)函數(shù)中的this指向。
對于不支持 ES5 的瀏覽器,我們可以對 forEach 進行簡單的擴展來兼容老的瀏覽器:
if (!Array.prototype.forEach) {
Array.prototype.forEach = function (callback, thisArg) {
for (var i=0; i < this.length; i++) {
// 當(dāng)thisArg為undefined時,JS引擎會將window作為其調(diào)用者
callback.call(thisArg, this[i], i, this.toString());
}
}
}
2、filter( callback [, thisArg] )
filter 是`過濾`的意思,所以這個方法的作用就是返回一個匹配過濾條件的新數(shù)組,其接收兩個參數(shù) callback 和 thisArg,callback 也是回調(diào)函數(shù),主要用于對元素進行條件匹配,thisArg 和 forEach 中的 thisArg 作用一樣,在這里就不重復(fù)了,看下面示例:
var arr = ["a", "b", "a", "c"];
var newArr = arr.filter(function (item) {
return item === "a";
});
newArr > ["a", "a"]
沒有filter的時候,要實現(xiàn)這個功能,我們事先要創(chuàng)建一個空的數(shù)組,把匹配到的元素再 push 進去,現(xiàn)在就不需要那么麻煩了,我們再看看對filter的擴展:
if (!Array.prototype.filter) {
Array.prototype.filter = function (callback, thisArg) {
var temp = [];
for (var i = 0; i < this.length; i++) {
if (callback.call(thisArg, this[i])){
// 如果callback返回true,則該元素符合過濾條件,將元素壓入temp中
temp.push(this[i]);
}
}
return temp;
}
}
可以看出,filter 將過濾的結(jié)果作為一個新數(shù)組返回,即使符合條件的元素只有一個,返回的也是數(shù)組 。為了更方便的對單個元素進行查詢,ES6 在數(shù)組原型上提供了 find 方法,用于從數(shù)組中查詢單個符合條件的元素,和 filter 不同的是,它返回的是單個元素。
[2, 3, 5, 8, 9, 3].find(item => item == 3); // 3
需要注意的是,find 只返回第一個匹配到的元素,如果沒有匹配到,則會返回 undefined 。和 filter 一樣,find 也可以傳遞第 2 個參數(shù),用于設(shè)置回調(diào)函數(shù)的 this 指針 。
3、map( callback[,thisArg] )
map 的作用是對原數(shù)組進行加工處理后并將其作為一個新數(shù)組返回,該方法同樣接收兩個參數(shù),callback 是回調(diào)函數(shù)用于對數(shù)組進行加工處理,thisArg 和上面的一樣。先看一個簡單的例子:
var arr = [
{w: 10, h: 10}, //定義長和寬
{w: 15, h: 20},
{w: 12, h: 12}
];
var newArr = arr.map(function (item) {
// 根據(jù)長寬計算出面積并賦值給新屬性area
item.area = item.w * item.h;
return item;
});
newArr[0] > {w: 10, h: 10, area: 100}
可以看出,newArr 返回的是增加了 area 屬性的對象數(shù)組。這個方法非常實用,一般情況下,當(dāng)一個ajax請求返回時,我們都要對其結(jié)果集進行過濾和校驗等操作,這時 map 就派上用場了。我們再看看如果對 map 進行兼容性擴展:
if (!Array.prototype.map) {
Array.prototype.map = function (callback, thisArg) {
var temp = [];
for (var i = 0; i < this.length; i++) {
var newItem = callback.call(thisArg, this[i]);
temp.push(newItem); // 將callback返回的新元素壓入temp中
}
return temp;
}
}
4、reduce ( callback[,initialValue] )
reduce 在這里有`減少`的意思,其作用是對數(shù)組進行歸并操作,換句話說就是對數(shù)組每一個元素進行累加,最終返回所有元素之和。 回調(diào)函數(shù) callback 接收4個參數(shù):
previousValue - 存放的是上一次callback返回的結(jié)果,其初始值默認為數(shù)組的第一個元素。
currentValue - 是當(dāng)前元素 。默認從數(shù)組的第二個元素開始。
currentIndex - 是當(dāng)前元素位置 。
array - 是當(dāng)前數(shù)組。
var arr = [1, 2, 3, 4];
var newArr = arr.reduce(function (previousValue, currentValue, currentIndex, array) {
console.log(previousValue, currentValue, currentIndex);
return previousValue + currentValue;
});
1 2 1
3 3 2
6 4 3
newArr > 10
reduce 除過可以傳遞 callback 之外,還可以傳遞一個參數(shù) initialValue ,作為數(shù)組累加的基數(shù)。當(dāng)傳了這個參數(shù)以后,callback 中的 previousValue 初始值就被置為 initialValue,reduce 也改為從數(shù)組的第一個元素開始遍歷。
var arr = [1, 2, 3, 4];
var newArr = arr.reduce(function (previousValue, currentValue, currentIndex, array){
console.log(previousValue, currentValue, currentIndex);
return previousValue + currentValue;
}, 100);
100 1 0
101 2 1
103 3 2
106 4 3
newArr > 110
從結(jié)果可以看出,reduce 最終返回的是: previousValue + 數(shù)組本身歸并計算的結(jié)果。對 reduce 的 polyfill 實現(xiàn)如下:
if (!Array.prototype.reduce) {
Array.prototype.reduce = function (callback, initialValue) {
var previousValue = initialValue || this[0];// 如果不指定intialValue,則默認為數(shù)組的第一個元素
// 如果不指定initialValue,i從1開始遍歷,否則就從0開始遍歷
for (var i = initialValue ? 0 : 1; i < this.length; i++) {
// previousValue 累加每一次返回的結(jié)果
previousValue = callback(previousValue, this[i], i, this.toString());
}
return previousValue;
}
}
5、reduceRight ( callback[,initialValue] )
和 reduce 的作用完全相同,唯一的不同是,reduceRight 是從右至左遍歷數(shù)組的元素。
6、some ( callback[,thisArg] )
some 是`某些、一些`的意思,其作用是對數(shù)組中的每一項執(zhí)行回調(diào)函數(shù),如果該函數(shù)對任一項返回 true,則停止遍歷,并返回 true 。
var arr = [ 1, 2, 3, 4];
var result = arr.some(function(item, index, array ){
console.log(item, index, array);
return item > 2;
});
>
1 0 [1, 2, 3, 4]
2 1 [1, 2, 3, 4]
3 2 [1, 2, 3, 4]
restule > true
some 檢測整個數(shù)組,只要當(dāng)arr中有一個元素符合條件 item>2 就停止檢測和遍歷,并返回 true,以表示檢測到目標(biāo)。這和我們在 for 循環(huán)中使用 break 語言的作用有點類似。 對于 some 的兼容性擴展如下:
if(!Array.prototype.some) {
Array.prototype.some = function (callback, thisArg) {
for (var i = 0; i < this.length; i++) {
if(callback.call(thisArg, this[i], i, this.toString())){
return true; // 檢測到callback返回true,跳出循環(huán),并返回true
}
}
return false; // 一個符合條件的都沒有檢測到,返回false
}
}
7、every (callback[,thisArg])
every 是`每一個`的意思,其作用是對數(shù)組中的每一項執(zhí)行回調(diào)函數(shù),如果該函數(shù)對每一項都返回 true,則返回 true 。
var arr = [ 1, 2, 3, 4];
var result = arr.every(function(item, index, array ){
console.log(item, index, array);
return item < 3;
});
1 0 [1, 2, 3, 4]
2 1 [1, 2, 3, 4]
3 2 [1, 2, 3, 4]
result > false
當(dāng)檢測第3個元素時,item<3 為 false,停止檢測,并返回 false,這說明every在檢測元素時,要求每一個元素都要符合條件 item<3,如果有一個不符合就停止檢測,并返回false。(你可以測試 item<5 時的運行結(jié)果,返回值一定是 true ) 。
那 every 到底有什么作用呢? 當(dāng)一個 for 循環(huán)使用了 break 語句后,我們想知道 for 循環(huán)是否正常的執(zhí)行完時, 我們一般會通過檢測for中的索引 i==arr.length 來判斷,因此every 的作用就體現(xiàn)在這里。
下面是對于 every 的兼容性擴展:
if (!Array.prototype.every) {
Array.prototype.every = function (callback, thisArg) {
for (var i = 0; i < this.length; i++) {
if(!callback.call(thisArg,this[i], i, this.toString())){
return false; // 檢測到不符合條件的元素,跳出循環(huán),并返回false
}
}
return true; // 所有元素都符合條件,返回true
}
}
8、indexOf[searchElement[, fromIndex]]
indexOf() 用于查詢數(shù)組元素對應(yīng)的索引位置,可以傳遞兩個參數(shù),第一個參數(shù)是要匹配的元素,必須是簡單數(shù)據(jù)類型。第二個參數(shù)是指定查詢的起始位置。
// 默認從索引0的位置開始 [1, 2, 3, 5, 2].indexOf(2); // 1 // 指定從索引3的位置開始 [1, 2, 3, 5, 2].indexOf(2, 3); // 4
indexOf() 返回的是元素在數(shù)組中的位置 。如果只想知道數(shù)組中是否存在某個元素,而不關(guān)心元素的位置,也可以使用 ES6 提供的 includes() 方法來判斷。
let a = [1, 2, 3]; a.includes(1); // true a.includes(1, 1);// false
includes() 也是數(shù)組原型上的方法, 和 indexOf() 的傳參是一樣的。
需要注意的是,indexOf() 適用于數(shù)組元素是簡單類型的情況,而無法檢索對象數(shù)組的元素位置。
let arr = [{c: 1}, {c: 2}]; // 對象數(shù)組
arr.indexOf({c: 1}); // -1
對于這個問題,可以使用 forEach() 來遍歷數(shù)組,當(dāng)找到符合條件的元素時,就可以獲取到對應(yīng)的數(shù)組下標(biāo),而在 ES6 中,可以使用 findIndex() 達到同樣的目的。
findIndex() 也是用于查詢數(shù)組元素的位置,和 indexOf() 不同的是,它可以檢索對象數(shù)組的元素位置,但需要通過回調(diào)函數(shù)來指定匹配的元素。
//簡單數(shù)組
[1, 2, 3, 5].findIndex(item => item == 3); // 2
//對象數(shù)組
[{id: 1}, {id: 3}, {id: 5}].findIndex(item => item.id == 3); // 1
9、lastIndexOf[searchElement[, fromIndex]]
和 indexOf() 的作用完全相同,唯一的不同是,lastIndexOf() 是從右至左檢索數(shù)組元素。
其他常用 API
1、sort( [compareFunction] )
對數(shù)組做原地排序,并返回這個數(shù)組,默認按照字符串 UNICODE 的碼位點排序,如下所示:
var fruit = ['cherries', 'apples', 'bananas']; fruit.sort(); // ['apples', 'bananas', 'cherries'] var scores = [1, 10, 2, 21]; scores.sort(); // [1, 10, 2, 21]
這顯然不是我們想要的結(jié)果。為了實現(xiàn)真正意義上的排序,可以給 sort 傳遞一個回調(diào)函數(shù) compareFunction, 其接收兩個參數(shù)a和 b,分別代表數(shù)組中待比較的兩個元素,a 元素 排在 b 的前面 。
回調(diào)函數(shù)需要返回一個表達式,用以標(biāo)明 升序 或 降序 操作:
return a - b:如果表達式 a - b 為真,觸發(fā)交換操作。也就是說, 當(dāng) a > b 時,進行元素交換,讓較小的元素 b 排在較大的元素 a 前面,即 升序操作。return b - a:如果表達式 b - a 為真,觸發(fā)交換操作。也就是說, 當(dāng) a < b 時,進行元素交換,讓較大的元素 b 排在較小的元素 a 前面,即 降序操作。
var numbers = [2, 4, 1, 10, 3];
// 回調(diào)參數(shù) a,b 是數(shù)組要比較的兩個元素,a 排在 b 的前面。
numbers.sort(function(a, b){
// 當(dāng) a > b 時觸發(fā)交換操作,把較小的排在前面,即升序。
return a - b;
});
> [1,2,3,4,10]
numbers.sort(function(a,b){
// 當(dāng) a < b 時觸發(fā)交換操作,把較大的排在前面,即降序。
return b - a;
});
> [10,4,3,2,1]
2、join( [separator] )
將數(shù)組中的所有元素連接成一個字符串。separtor 用于指定連接每個數(shù)組元素的分隔符。分隔符會被轉(zhuǎn)成字符串類型;如果省略的話,默認為一個逗號。如果separtor 是一個空字符串,那么數(shù)組中的所有元素將被直接連接。
var data = ['Wind', 'Rain', 'Fire'];
data.join(); // Wind,Rain,Fire
data.join(', '); // Wind, Rain, Fire
data.join(' + '); // Wind + Rain + Fire
data.join(''); // WindRainFire
3、concat( value1,...,valueN )
concat 方法將創(chuàng)建一個新的數(shù)組,然后將調(diào)用它的對象(this 指向的對象)中的元素以及所有參數(shù)中的數(shù)組類型的參數(shù)中的元素以及非數(shù)組類型的參數(shù)本身按照順序放入這個新數(shù)組,并返回該數(shù)組, valueN 允許是數(shù)組或非數(shù)組值。在沒有給 concat 傳遞參數(shù)的情況下,它只是復(fù)制當(dāng)前數(shù)組并返回副本。
var alpha = ['a', 'b', 'c']; alpha.concat(1, [2, 3]); //["a", "b", "c", 1, 2, 3] alpha.concat(); // ['a', 'b', 'c']
4、push( element1,...,elementN ) 和 pop( )
push 添加一個或多個元素到數(shù)組的末尾,并返回數(shù)組新的長度;pop刪除一個數(shù)組中的最后的一個元素,并且返回這個元素。
var data = [1, 2, 3]; data.push(4, 5, 6); // 6 > 數(shù)組的長度 data > [1,2,3,4,5,6] data.pop(); //6 > 出棧的元素 data > [1,2,3,4,5]
注意:push 和 pop 并不會改變原來的元素位置。
5、unshift( element1, ..., elementN ) 和 shift( )
unshift 添加一個或多個元素到數(shù)組的開頭,并返回數(shù)組新的長度;shift 刪除一個數(shù)組中的第一個元素,并且返回這個元素。
var data = [1, 2, 3]; data.unshift(-1, -2, -3); // 6 > 新數(shù)組的長度 data > [-1,-2,-3,1,2,3] data.shift(); // -1 > 被移除的元素 data > [-2,-3,1,2,3]
注意:unshift 和 shift 都會改變原來的元素位置。
如果把數(shù)組看成一個棧,push 和 pop 、unshift 和 shift 對數(shù)組的操作行為就符合 后進先出 (LIFO) 規(guī)律 ;如果把數(shù)組看成一個隊列,push 和 shift 、unshift 和 pop 對數(shù)組的操作行為就符合 先進先出 (FIFO) 規(guī)律。
因此,可以使用數(shù)組來實現(xiàn)對應(yīng)的數(shù)據(jù)結(jié)構(gòu):棧 和 隊列。
6、slice( begin [, end] )
slice 方法從begin 的索引位置開始提取數(shù)組元素,到 end 位置結(jié)束,但不包括 end 位置的元素,如果 end 被省略,則默認提取到數(shù)組的結(jié)尾,如果結(jié)束位置小于起始位置,則返回空數(shù)組。
var data = [1, 2, 3]; data.slice(0); // [1,2,3] 提取的元素 data.slice(1, 2); // [2] 提取的元素 data.slice(2, 1); // []
如果參數(shù)中有一個負數(shù),則用數(shù)組長度加上該數(shù)來確定相應(yīng)的位置。例如,在一個包含 5 項的數(shù)組上調(diào)用 slice(-2, -1) 與調(diào)用 slice(3, 4) 得到的結(jié)果相同。
var t = [1, 2, 3, 4, 5]; t.slice(-2, -1); // [4] t.slice(3, 4); // [4]
slice方法會返回一個新的數(shù)組,由于數(shù)組是引用類型, 通常會通過 arr.slice(0) 來實現(xiàn)數(shù)組的 淺拷貝,但這只對 數(shù)組元素是基本類型 的情況有效。
// 簡單數(shù)組拷貝 var arr = [1, 2, 3, 4]; var cyarr = arr.slice(0); // 淺拷貝 arr.splice(3, 1); // 對原數(shù)組操作 console.log(arr, cyarr); //[1,2,3] , [1,2,3,4] > 拷貝成功
如果是對象數(shù)組,數(shù)組元素作為指針指向?qū)ο髢?nèi)存,slice(0) 僅僅是拷貝了一份指針作為副本,而副本中的指針,指向的還是原來的對象,因此,對一個數(shù)組中的對象做操作,另一個數(shù)組中的對象也會同步變化。
//對象數(shù)組拷貝
var list = [{name: 'zhangsan'}];
var cylist = list.slice(0); // 淺拷貝
list[0].name = 'lisi'; // 對原數(shù)組操作
console.log(list, cylist); // [{name:'lisi'}] , [{name:'lisi'}] -> 拷貝失敗
要實現(xiàn)數(shù)組的深拷貝,需要通過 JSON 的序列化和反序列化來實現(xiàn),即: JSON.parse( JSON.stringify(arr) )
//對象數(shù)組拷貝
var list = [{name: 'zhangsan'}];
var cylist = JSON.parse(JSON.stringify(list)); // 深拷貝
list[0].name = 'lisi'; // 對原數(shù)組操作
console.log(list, cylist); // [{name: 'lisi'}] , [{name: 'zhangsan'}] > 拷貝成功
7、splice( start, deleteCount[,value1,...,valueN] )
splice方法從一個數(shù)組中移除一個或多個元素,如果必要,在所移除元素的位置上插入新元素,返回所移除的元素。
data.splice(2, 1); // [3] > 被刪除的元素 data > [1,2] data.splice(2, 2, 4, 5); // [3] > 被刪除的元素 data > [1,2,4,5] data.splice(2, 2, 4, 5, 6); // [3] > 被刪除的元素 data > [1,2,4,5,6]
原創(chuàng)發(fā)布 @一像素 2016.01


浙公網(wǎng)安備 33010602011771號