前面的話
??數(shù)組是一種基礎的
JS
對象,隨著時間推進,JS
中的其他部分一直在演進,而直到ES5
標準才為數(shù)組對象引入一些新方法來簡化使用瑰步。ES6
標準繼續(xù)改進數(shù)組钞速,添加了很多新功能鱼炒。本文將詳細介紹ES6
數(shù)組擴展
靜態(tài)方法
??在
ES6
以前,創(chuàng)建數(shù)組的方式主要有兩種娇未,一種是調(diào)用Array
構造函數(shù)责静,另一種是用數(shù)組字面量語法袁滥,這兩種方法均需列舉數(shù)組中的元素,功能非常受限灾螃。如果想將一個類數(shù)組對象(具有數(shù)值型索引和length
屬性的對象)轉(zhuǎn)換為數(shù)組题翻,可選的方法也十分有限,經(jīng)常需要編寫額外的代碼腰鬼。為了進一步簡化JS
數(shù)組的創(chuàng)建過程嵌赠,ES6
新增了Array.of()
和Array.from()
兩個方法
【Array.of()】
ES6
之所以向JS
添加新的創(chuàng)建方法塑荒,是要幫助開發(fā)者們規(guī)避通過Array
構造函數(shù)創(chuàng)建數(shù)組時的怪異行為
let items =new Array(2);
console.log(items.length); // 2
console.log(items[0]);// undefined
console.log(items[1]);// undefined
items =newArray("2");
console.log(items.length); // 1
console.log(items[0]);// "2"
items =new Array(1, 2);
console.log(items.length); // 2
console.log(items[0]);// 1
console.log(items[1]);// 2
items =new Array(3, "2");
console.log(items.length); // 2
console.log(items[0]);// 3
console.log(items[1]);// "2"
如果給
Array
構造函數(shù)傳入一個數(shù)值型的值,那么數(shù)組的length
屬性會被設為該值姜挺。如果傳入多個值齿税,此時無論這些值是不是數(shù)值型的,都會變?yōu)閿?shù)組的元素初家。這個特性令人感到困惑偎窘,不可能總是注意傳入數(shù)據(jù)的類型,所以存在一定的風險ES6
通過引入Array.of()
方法來解決這個問題溜在。Array.of()
與Array構造函數(shù)的工作機制類似,只是不存在單一數(shù)值型參數(shù)值的特例他托,無論有多少參數(shù)掖肋,無論參數(shù)是什么類型的,Array.of()
方法總會創(chuàng)建一個包含所有參數(shù)的數(shù)組
let items = Array.of(1, 2);
console.log(items.length); // 2
console.log(items[0]);// 1
console.log(items[1]);// 2
items = Array.of(2);
console.log(items.length); // 1
console.log(items[0]);// 2
items = Array.of("2");
console.log(items.length); // 1
console.log(items[0]);// "2"
- 要用
Array.of()
方法創(chuàng)建數(shù)組赏参,只需傳入希望在數(shù)組中包含的值志笼。第一個示例創(chuàng)建了一個包含兩個數(shù)字的數(shù)組;第二個數(shù)組包含一個數(shù)宇把篓;最后一個數(shù)組包含一個字符串纫溃。這與數(shù)組字面量的使用方法很相似,在大多數(shù)時候韧掩,可以用數(shù)組字面量來創(chuàng)建原生數(shù)組紊浩,但如果需要給一個函數(shù)傳入Array
的構造函數(shù),則可能更希望傳入Array.of()
來確保行為一致
function createArray(arrayCreator, value) {
return arrayCreator(value);
}
let items = createArray(Array.of, value);
- 在這段代碼中心
createArray()
函數(shù)接受兩個參數(shù)疗锐,一個是數(shù)組創(chuàng)造者函數(shù)坊谁,另一個是要插入數(shù)組的值』可以傳入Array.of()
作為createArray()
方法的第一個參數(shù)來創(chuàng)建新數(shù)組口芍,如果不能保證傳入的值一定不是數(shù)字,那么直接傳入Array會非常危險
[注意] Array.of()
方法不通過Symbol.species
屬性確定返回值的類型雇卷,它使用當前構造函數(shù)(也就是of()
方法中的this
值)來確定正確的返回數(shù)據(jù)的類型
【Array.from()】
??
JS
不支持直接將非數(shù)組對象轉(zhuǎn)換為真實數(shù)組鬓椭,arguments
就是一種類數(shù)組對象,如果要把它當作數(shù)組使用則必須先轉(zhuǎn)換該對象的類型关划。在ES5
中小染,可能需要編寫如下函數(shù)來把類數(shù)組對象轉(zhuǎn)換為數(shù)組
function makeArray(arrayLike) {
var result = [];
for(vari = 0, len = arrayLike.length; i < len; i++) {
result.push(arrayLike[i]);
}
return result;
}
function doSomething() {
var args = makeArray(arguments); // 使用 args
}
- 這種方法先是手動創(chuàng)建一個
result
數(shù)組,再將arguments
對象里的每一個元素復制到新數(shù)組中祭玉。盡管這種方法有效氧映,但需要編寫很多代碼才能完成如此簡單的操作。最終脱货,開發(fā)者們發(fā)現(xiàn)了一種只需編寫極少代碼的新方法岛都,調(diào)用數(shù)組原生的slice()
方法可以將非數(shù)組對象轉(zhuǎn)換為數(shù)組
function makeArray(arrayLike) {
return Array.prototype.slice.call(arrayLike);
}
function doSomething() {
var args = makeArray(arguments); // 使用 args
}
這段代碼的功能等價于之前的示例律姨,將
slice()
方法執(zhí)行時的this值設置為類數(shù)組對象,而slice()
對象只需數(shù)值型索引和length
屬性就能夠正確運行臼疫,所以任何類數(shù)組對象都能被轉(zhuǎn)換為數(shù)組盡管這項技術不需要編寫很多代碼择份,但是我們調(diào)用
Array.prototype.slice.call(arrayLike)
時不能直覺地想到這是在將arrayLike
轉(zhuǎn)換成一個數(shù)組。所幸烫堤,ES6
添加了一個語義清晰荣赶、語法簡潔的新方法Array.from()
來將對象轉(zhuǎn)化為數(shù)組Array.from()
方法可以接受可迭代對象或類數(shù)組對象作為第一個參數(shù),最終返回一個數(shù)組
function doSomething() {
var args = Array.from(arguments); // 使用 args
}
-
Array.from()
方法調(diào)用會基于arguments
對象中的元素創(chuàng)建一個新數(shù)組鸽斟,args
是Array
的一個實例拔创,包含arguments
對象中同位置的相同值
[注意] Array.from()
方法也是通過this來確定返回數(shù)組的類型的
映射轉(zhuǎn)換
??如果想要進一步轉(zhuǎn)化數(shù)組,可以提供一個映射函數(shù)作為
Array.from()
的第二個參數(shù)富蓄,這個函數(shù)用來將類數(shù)組對象中的每一個值轉(zhuǎn)換成其他形式剩燥,最后將這些結果儲存在結果數(shù)組的相應索引中
function translate() {
return Array.from(arguments, (value) => value + 1);
}
let numbers = translate(1, 2, 3);
console.log(numbers); // 2,3,4
- 在這段代碼中,為
Array.from()
方法傳入映射函數(shù)(value)=>value+1
立倍,數(shù)組中的每個元素在儲存前都會被加1灭红。如果用映射函數(shù)處理對象,也可以給Array.from()
方法傳入第三個參數(shù)來表示映射函數(shù)的this
值
let helper ={
diff: 1,
add(value) {
return value +this.diff;
}
};
function translate() {
return Array.from(arguments, helper.add, helper);
}
let numbers = translate(1, 2, 3);
console.log(numbers); // 2,3,4
此示例傳入
helper.add()
作為轉(zhuǎn)換用的映射函數(shù)口注,由于該方法使用了this.diff
屬性变擒,因此需要為Array.from()
方法提供第三個參數(shù)來指定this
的值,從而無須通過調(diào)用bind()
方法或其他方式來指定this
的值了用Array.from()
轉(zhuǎn)換可迭代對象Array.from()
方法可以處理類數(shù)組對象和可迭代對象寝志,也就是說該方法能夠?qū)⑺泻?code>Symbol.iterator屬性的對象轉(zhuǎn)換為數(shù)組
let numbers = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
}
};
let numbers2 = Array.from(numbers, (value) => value + 1);
console.log(numbers2); // 2,3,4
- 由于
numbers
是一個可迭代對象娇斑,因此可以直接將它傳入Array.from()
來轉(zhuǎn)換成數(shù)組。此處的映射函數(shù)將每一個數(shù)字加1澈段,所以結果數(shù)組最終包含的值為2悠菜、3和4
[注意]如果一個對象既是類數(shù)組又是可迭代的,那么Array.from()
方法會根據(jù)迭代器來決定轉(zhuǎn)換哪個值
實例方法
ES6
延續(xù)了ES5
的一貫風格败富,也為數(shù)組添加了幾個新的方法:includes()
方法返回一個布爾值悔醋,表示數(shù)組是否包含給定的值;find()
方法和findIndex()
方法可以協(xié)助開發(fā)者在數(shù)組中查找任意值兽叮;fill()
方法和copyWithin()
方法的靈感則來自于定型數(shù)組的使用過程芬骄,定型數(shù)組也是ES6
中的新特性,是一種只包含數(shù)字的數(shù)組
【includes()】
-
Array.prototype.includes
方法返回一個布爾值鹦聪,表示某個數(shù)組是否包含給定的值账阻,與字符串的includes
方法類似。ES2016引入了該方法
[1, 2, 3].includes(2)// true
[1, 2, 3].includes(4)// false
[1, 2, NaN].includes(NaN)// true
- 該方法的第二個參數(shù)表示搜索的起始位置泽本,默認為0淘太。如果第二個參數(shù)為負數(shù),則表示倒數(shù)的位置,如果這時它大于數(shù)組長度(比如第二個參數(shù)為-4蒲牧,但數(shù)組長度為3)撇贺,則會重置為從0開始
[1, 2, 3].includes(3, 3);// false
[1, 2, 3].includes(3, -1);// true
- 沒有該方法之前,我們通常使用數(shù)組的
indexOf
方法冰抢,檢查是否包含某個值
if(arr.indexOf(el) !== -1) {
// ...
}
-
indexOf
方法有兩個缺點松嘶,一是不夠語義化,它的含義是找到參數(shù)值的第一個出現(xiàn)位置挎扰,所以要去比較是否不等于-1翠订,表達起來不夠直觀。二是遵倦,它內(nèi)部使用嚴格相等運算符(===)進行判斷尽超,這會導致對NaN的誤判
[NaN].indexOf(NaN)// -1
-
includes
使用的是不一樣的判斷算法,就沒有這個問題
[NaN].includes(NaN)// true
- 下面代碼用來檢查當前環(huán)境是否支持該方法骇吭,如果不支持橙弱,部署一個簡易的替代版本
const contains = (() =>
Array.prototype.includes ? (arr, value) =>
arr.includes(value) : (arr, value) =>
arr.some(el => el ===value)
)();
contains(['foo', 'bar'], 'baz');// => false
- 另外,
Map
和Set
數(shù)據(jù)結構有一個has
方法燥狰,需要注意與includes
區(qū)分
1、Map
結構的has
方法斜筐,是用來查找鍵名的龙致,比如Map.prototype.has(key)
、WeakMap.prototype.has(key)
顷链、Reflect.has(target,propertyKey)
2目代、Set
結構的has
方法,是用來查找值的嗤练,比如Set.prototype.has(value)
榛了、WeakSet.prototype.has(value)
【find()和findIndex()】
??由于沒有內(nèi)建的數(shù)組搜索方法,因此ES5正式添加了
indexOf()
和lastIndexOf()
兩個方法煞抬,可以用它們在數(shù)組中查找特定的值霜大。雖然這是一個巨大的進步,但這兩種方法仍有局限之處革答,即每次只能查找一個值战坤,如果想在系列數(shù)字中查找第一個偶數(shù),則必須自己編寫代碼來實現(xiàn)残拐。于是ES6引入了find()
方法和findIndex()
方法來解決這個問題
find()
方法和findIndex()
方法都接受兩個參數(shù):一個是回調(diào)函數(shù)途茫;另一個是可選參數(shù),用于指定回調(diào)函數(shù)中this
的值溪食。執(zhí)行回調(diào)函數(shù)時囊卜,傳入的參數(shù)分別為數(shù)組中的某個元素、該元素在數(shù)組中的索引和數(shù)組本身,與傳入map()
和forEach()
方法的參數(shù)相同栅组。如果給定的值滿足定義的標準雀瓢,回調(diào)函數(shù)應返回true
。一旦回調(diào)函數(shù)返回true
笑窜,find()
方法和findIndex()
方法都會立即停止搜索數(shù)組剩余的部分二者間唯一的區(qū)別是致燥,
find()
方法返回查找到的值,findIndex()
方法返回查找到的值的索引
let numbers = [25, 30, 35, 40, 45];
console.log(numbers.find(n => n > 33));// 35
console.log(numbers.findIndex(n => n > 33));// 2
這段代碼通過調(diào)用
find()
方法和findIndex()
方法來定位numbers
數(shù)組中第一個比33大的值排截,調(diào)用find()
方法返回的是35嫌蚤,而調(diào)用findIndex()
方法返回的是35在numbeps
數(shù)組中的位置2如果要在數(shù)組中根據(jù)某個條件查找匹配的元素,那么
find()
方法和findIndex()
方法可以很好地完成任務断傲;如果只想查找與某個值匹配的元素脱吱,則indexOf()
方法和lastIndexOf()
方法是更好的選擇
【fill()】
-
fill()
方法可以用指定的值填充一至多個數(shù)組元素。當傳入一個值時认罩,fill()
方法會用這個值重寫數(shù)組中的所有值
let numbers = [1, 2, 3, 4];
numbers.fill(1);
console.log(numbers.toString()); // 1,1,1,1
- 在此示例中箱蝠,調(diào)用
numbers.fill(1)
方法后numbers
中所有的值會變成1,如果只想改變數(shù)組某一部分的值垦垂,可以傳入開始索引和不包含結束索引(不包含結束索引當前值)這兩個可選參數(shù)
let numbers = [1, 2, 3, 4];
numbers.fill(1, 2);
console.log(numbers.toString()); // 1,2,1,1
numbers.fill(0, 1, 3);
console.log(numbers.toString()); // 1,0,0,1
- 在
numbers.fill(1,2)
調(diào)用中宦搬,參數(shù)2表示從索引2開始填充元素,由于未傳入第三個參數(shù)作為不包含結束索引劫拗,因此使用numbers.length
作為不包含結束索引间校,因而numbers數(shù)組的最后兩個元素被填充為1。操作numbers.fill(0,1,3)
會將數(shù)組中位于索引1和2的元素填充為0页慷。調(diào)用fill()時若傳入第二個和第三個參數(shù)則可以只填充數(shù)組中的部分元素
[注意]如果開始索引或結束索引為負值憔足,那么這些值會與數(shù)組的length
屬性相加來作為最終位置。例如酒繁,如果開始位置為-1滓彰,那么索引的值實際為array.length-1
,array
為調(diào)用fill()
方法的數(shù)組
【copyWithin()】
??
copyWithin()
方法與fill()
方法相似州袒,其也可以同時改變數(shù)組中的多個元素揭绑。fill()
方法是將數(shù)組元素賦值為一個指定的值,而copyWithin()
方法則是從數(shù)組中復制元素的值稳析。調(diào)用copyWithin()
方法時需要傳入兩個參數(shù):一個是該方法開始填充值的索引位置洗做,另一個是開始復制值的索引位置
- 比如復制數(shù)組前兩個元素的值到后兩個元素
let numbers = [1, 2, 3, 4];
// 從索引 2 的位置開始粘貼
// 從數(shù)組索引 0 的位置開始復制數(shù)據(jù)numbers.copyWithin(2, 0);
console.log(numbers.toString()); // 1,2,1,2
這段代碼從
numbers
的索引2開始粘貼值,所以索引2和3將被重寫彰居。給CopyWithin()
傳入第二個參數(shù)0表示诚纸,從索引0開始復制值并持續(xù)到?jīng)]有更多可復制的值默認情況下,
copyWithin()
會一直復制直到數(shù)組末尾的值陈惰,但是可以提供可選的第三個參數(shù)來限制被重寫元素的數(shù)量畦徘。第三個參數(shù)是不包含結束索引毕籽,用于指定停止復制值的位置
let numbers = [1, 2, 3, 4];
// 從索引 2 的位置開始粘貼
// 從數(shù)組索引 0 的位置開始復制數(shù)據(jù)
// 在遇到索引 1 時停止復制
numbers.copyWithin(2, 0, 1);
console.log(numbers.toString()); // 1,2,1,4
- 在這個示例中,由于可選的結束索引被設置為了1井辆,因此只有位于索引0的值被復制了关筒,數(shù)組中的最后一個元素保持不變
[注意]正如fill()
方法一樣,copyWithin()
方法的所有參數(shù)都接受負數(shù)值杯缺,并且會自動與數(shù)組長度相加來作為最終使用的索引
-
fill()
和copyWithin()
這兩個方法起源于定型數(shù)組蒸播,為了保持數(shù)組方法的一致性才添加到常規(guī)數(shù)組中的。如果使用定型數(shù)組來操作數(shù)字的比特萍肆,這些方法將大顯身手