【重溫基礎】10.陣列

pingan8787發表於2018-12-24

本文是 重溫基礎 系列文章的第十篇。
今日感受:平安夜,多棒。

系列目錄:

本章節複習的是JS中的陣列,以索引進行排序。

前置知識:
陣列是一個有序的資料集合,使用陣列名稱和索引進行訪問。

let arr = [1,2,3];
arr[0] = 1;
複製程式碼

在JavaScript中陣列沒有明確資料型別。

let arr = [1, 'hi', undefined, fun()];
複製程式碼

1.建立陣列

建立陣列方法有3種:

let arr = new Array(ele1, ele2, ele3, ..., eleN);
let arr = Array(ele1, ele2, ele3, ..., eleN);
let arr = [ele1, ele2, ele3, ..., eleN];
複製程式碼

上面是已知陣列元素,另外一種還有建立一個長度不為0,且有沒有任何元素的陣列

let len = 5;

let arr = new Array(len); // 方法1
let arr = Array(len);     // 方法2
let arr = [];             // 方法3
arr.length = len;
複製程式碼

若傳入的陣列長度不是整數,則報錯:

let arr = new Array(3.5); 
let arr = Array(3.5); 
let arr = [];
arr.length = 3.5;
//Uncaught RangeError: Invalid array length
複製程式碼

其中要注意這兩種建立方法是不同的:

let arr1 = new Array(4);   // [empty × 4]
let arr2 = [4];            // [4]
for(let k in arr1){
    console.log(k);
}  // undefined
for(let k in arr2){
    console.log(k);
}  // 0
複製程式碼

2.使用陣列

2.1 簡單使用

獲取陣列指定位置的值:

let a = [1,2,5];
a[0];  // 1
a[2];  // 5
a[3];  // undefined
複製程式碼

獲取陣列長度:

let a = [1,2,5];
a.length;    // 3
a["length"]; // 3
複製程式碼

設定陣列指定位置的值:

let a = [1,2,5];
a[0] = 9;
a[2] = 99;
a[3] = 999;
複製程式碼

2.2 理解陣列length

  • 陣列的索引值是從0開始,即上面陣列索引0的是1,索引1的值是2,依次下去。
  • 陣列length永遠返回的是陣列最後一個元素的索引加1。
  • 可通過arr.length = 0來清空陣列。
  • 可通過arr.length = len來設定陣列長度。

2.3 遍歷陣列

遍歷陣列就是以某種方法處理陣列的每個元素,簡單如下:

  • 使用for迴圈:
let arr = ["pingan", "leo", "robin"];
for (let i = 0; i<arr.length; i++){
    // 處理元素的操作
    console.log(`第${i}個元素是:${arr[i]};`)
}
// 第0個元素是:pingan;
// 第1個元素是:leo;
// 第2個元素是:robin;
複製程式碼
  • 使用for...in
let arr = ["pingan", "leo", "robin"];
for(let i in arr){
    console.log(`第${i}個元素是:${arr[i]};`)
}
// 第0個元素是:pingan;
// 第1個元素是:leo;
// 第2個元素是:robin;
複製程式碼
  • 使用forEach
    arr.forEach(callback) 接收一個回撥方法。
    callback(val, index, array) : 接收三個引數:
    • val : 當前處理的元素;
    • index : 當前處理的元素的索引;
    • array : 正在處理的陣列;

可參考MDN Array.prototype.forEach 的詳細介紹。

let arr = ["pingan", "leo", "robin"];
arr.forEach(function(val, i, array){
    console.log(`第${i}個元素是:${val};`)
})
複製程式碼

3. 陣列方法(訪問和修改)

方法名稱 方法介紹
concat() 連線兩個或更多的陣列,並返回結果。
join() 把陣列的所有元素放入一個字串。元素通過指定的分隔符進行分隔。
pop() 刪除並返回陣列的最後一個元素
push() 向陣列的末尾新增一個或更多元素,並返回新的長度。
reverse() 顛倒陣列中元素的順序。
shift() 刪除並返回陣列的第一個元素
slice() 從某個已有的陣列返回選定的元素
sort() 對陣列的元素進行排序
splice() 刪除元素,並向陣列新增新元素。
toSource() 返回該物件的原始碼。
toString() 把陣列轉換為字串,並返回結果。
toLocaleString() 把陣列轉換為本地陣列,並返回結果。
unshift() 向陣列的開頭新增一個或更多元素,並返回新的長度。
valueOf() 返回陣列物件的原始值
indexOf() 在陣列中搜尋指定元素並返回第一個匹配的索引
lastIndexOf() 在陣列中搜尋指定元素並返回最後一個匹配的索引

可參考W3school JavaScript Array 物件 的詳細介紹。

3.1 concat()

連線兩個或更多的陣列,並返回一個新陣列。

  • 語法:
    arr.concat(a1, a2, ..., an);
  • 引數:
    arr:目標陣列;
    a1,a2,...,an:需要合併的元素;
let a1 = [1,2,3];
let a2 = [9,99,999];
let a = a1.concat(a2);
// [1, 2, 3, 9, 99, 999]
複製程式碼

3.2 join()

使用指定分隔符,連線兩個或多個陣列的元素,返回一個字串。

  • 語法:
    arr.join(sep);
  • 引數:
    arr:目標陣列;
    sep:連線的分隔符,預設值為“,”;
let arr = ["pingan", "leo", "robin"];
arr.join();    // "pingan,leo,robin"
arr.join("");  // "pinganleorobin"
arr.join(","); // "pingan,leo,robin"
複製程式碼

3.3 pop()和push()

  • pop(): 刪除並返回陣列最後一個元素改變原陣列

  • push(item): 向陣列末尾新增一個或多個元素,改變原陣列,返回新的陣列長度。
    方便記憶和理解:兩個都是從陣列末尾操作,pop()是刪除最後一個元素,push()是向最後一位新增新的元素。

let arr = ["pingan", "leo"];
let a1 = arr.pop();              // "leo"
let a2 = arr.push("robin","hi"); // 3
arr;   // ["pingan", "robin", "hi"]
複製程式碼

3.4 shift()和unshift()

  • shift(): 刪除並返回陣列第一個元素改變原陣列
  • unshift(item): 向陣列頭部新增一個或多個元素,改變原陣列,返回新的陣列長度。

方便記憶和理解:兩個都是從陣列頭部操作,shift()是刪除第一個元素,unshift()是向第一位新增新的元素。

let arr = ["pingan", "leo"];
let a1 = arr.shift();               // "pingan"
let a2 = arr.unshift("robin","hi"); // 3
arr;   // ["robin", "hi", "leo"]
複製程式碼

3.5 reverse()

顛倒陣列中元素的順序,改變原陣列

let arr = [1, 2, 3, 4];
arr.reverse();  // [4, 3, 2, 1]
複製程式碼

3.6 slice()

用於提取陣列中一個片段,作為新陣列返回。
slice(start[,end]): 接收2個引數:

  • start: 必需,指定起始索引,若負數則從陣列最後開始算起,-1為倒數第一位,-2為倒數第二位,以此類推。
  • end: 可選,指定結束索引,若沒傳則表示到陣列結束。

注意
end若有指定的話,是不包含end索引上的值。

let arr = [1, 2, 3, 5, 6];
let a1 = arr.slice(2);    // [3, 5, 6]
let a2 = arr.slice(2,3);  // [3]
複製程式碼

3.7 splice()

從陣列中刪除指定索引開始的專案,然後返回被刪除的專案。

  • 語法: arr.splice(index, num, a1, a2,...,an);
  • 引數:
    index: 必需,起始位置的索引,若負數則從陣列最後開始算起;
    num:必需,刪除的數量,若為0則不刪除;
    a1,a2,...an:可選,為陣列新增的元素;
let arr = [1, 2, 3, 4];
let a = arr.splice(1, 2, "hi", "leo");
// a =>  [2, 3]
// arr =>  [1, "hi", "leo", 4]
複製程式碼

3.8 sort()

對陣列的元素進行排序,改變原陣列
可接受一個回撥方法作為比較函式,來決定排序方式。
比較函式應該具有兩個引數 ab,返回值如下:
a 小於 b,在排序後的陣列中 a 應該出現在 b 之前,則返回一個小於 0 的值。 若 a 等於 b,則返回 0。 若 a 大於 b,則返回一個大於 0 的值。

let a1 = [1,3,6,9,10];
a1.sort(); // [1, 10, 3, 6, 9]
a1.sort(function(a,b){
    return a > b ? 1 : a < b ? -1 : 0;
})         // [1, 3, 6, 9, 10]
複製程式碼

3.9 indexOf()和lastIndexOf()

兩者都是在陣列搜尋指定元素,只是indexOf()返回的是搜尋到的第一個元素的索引,而lastIndexOf()返回的是搜尋到的最後一個元素的索引。
語法:
indexOf(ele[,start])lastIndexOf(ele[,start]);
引數:

  • ele: 需要搜尋的元素。
  • start: 開始搜尋的索引。
let arr = ["hh1", "hh2", "hh2", "hh2", "hh3", "hh4"];
let a1 = arr.indexOf("hh2");      // 1
let a2 = arr.lastIndexOf("hh2");  // 3
let a3 = arr.indexOf("hh2",2);    // 2
複製程式碼

4. 陣列方法(迭代)

方法名稱 方法介紹
forEach() 為陣列中的每個元素執行一次回撥函式。
every() 如果陣列中的每個元素都滿足測試函式,則返回 true,否則返回 false。
some() 如果陣列中至少有一個元素滿足測試函式,則返回 true,否則返回 false。
filter() 將所有在過濾函式中返回 true 的陣列元素放進一個新陣列中並返回。
map() 返回一個由回撥函式的返回值組成的新陣列。
reduce() 從左到右為每個陣列元素執行一次回撥函式,並把上次回撥函式的返回值放在一個暫存器中傳給下次回撥函式,並返回最後一次回撥函式的返回值。
reduceRight() 從右到左為每個陣列元素執行一次回撥函式,並把上次回撥函式的返回值放在一個暫存器中傳給下次回撥函式,並返回最後一次回撥函式的返回值。

以下是ES6規範新增的陣列方法:

方法名稱 方法介紹
keys() 返回一個陣列迭代器物件,該迭代器會包含所有陣列元素的鍵。
values() 返回一個陣列迭代器物件,該迭代器會包含所有陣列元素的值。
entries() 返回一個陣列迭代器物件,該迭代器會包含所有陣列元素的鍵值對。
find() 找到第一個滿足測試函式的元素並返回那個元素的值,如果找不到,則返回 undefined。
findIndex() 找到第一個滿足測試函式的元素並返回那個元素的索引,如果找不到,則返回 -1。

可參考MDN Array 的詳細介紹。

4.1 forEach()

對陣列的每個元素執行一次提供的函式。

語法:
arr.forEach(callback)

引數:
callback(val, index, arr) : 需要執行的函式,接收三個引數:

  • val : 正在處理的當前元素;
  • index : 可選,正在處理的當前元素的索引;
  • arr : 可選,正在操作的陣列;
let a = [1,3,5,7];
a.forEach(function(val, index, arr){
    arr[index] = val * 2
})
a ; // [2, 6, 10, 14]
複製程式碼

4.2 every()

測試陣列的所有元素是否都通過了指定函式的測試。
語法:
arr.every(callback)

引數:
callback(val, index, arr) : 需要執行的函式,接收三個引數:

  • val : 正在處理的當前元素;
  • index : 可選,正在處理的當前元素的索引;
  • arr : 可選,正在操作的陣列;

返回值:
若都通過返回true,否則返回false

let a = [1, "", "aa", 13, 6];
let res = a.every(function(val, index, arr){
    return typeof val == "number";
})
res;// false

let b = [1, 2, 3];
let r = b.every(function(val, index, arr){
    return typeof val == "number";
})
r;  // true
複製程式碼

4.3 some()

測試陣列中的某些元素是否通過由提供的函式實現的測試。
語法:
arr.some(callback)

引數:
callback(val, index, arr) : 需要執行的函式,接收三個引數:

  • val : 正在處理的當前元素;
  • index : 可選,正在處理的當前元素的索引;
  • arr : 可選,正在操作的陣列;

返回值:
若有一個通過返回true,否則返回false

let a = [1, "", "aa", 13, 6];
let res = a.some(function(val, index, arr){
    return typeof val == "number";
})
res;// true

let b = [1, 2, 3];
let r = b.some(function(val, index, arr){
    return typeof val == "number";
})
r;  // true
複製程式碼

4.4 filter()

將所有在過濾函式中返回 true 的陣列元素放進一個新陣列中並返回。

語法:
arr.filter(callback)

引數:
callback(val, index, arr) : 需要執行的函式,接收三個引數:

  • val : 正在處理的當前元素;
  • index : 可選,正在處理的當前元素的索引;
  • arr : 可選,正在操作的陣列;

返回值:
一個返回通過測試的元素的陣列,若都沒有則返回空陣列。

let a = [1, "", "aa", 13, 6];
let res = a.filter(function(val, index, arr){
    return typeof val == "number";
})
res;//[1, 13, 6]
複製程式碼

4.5 map()

傳入一個操作函式,對每個元素執行此方法,並返回一個執行後的陣列。

語法:
arr.map(callback)

引數:
callback(val, index, arr) : 需要執行的函式,接收三個引數:

  • val : 正在處理的當前元素;
  • index : 可選,正在處理的當前元素的索引;
  • arr : 可選,正在操作的陣列;

返回值:
一個新陣列,每個元素都是回撥函式的結果。

let a = [1, 3, 5];
let b = a.map(function(val, index, arr){
    return val + 2;
})
b; //[3, 5, 7]
複製程式碼

5. 陣列的擴充(ES6)

5.1 擴充運算子

擴充運算子使用(...),類似rest引數的逆運算,將陣列轉為用(,)分隔的引數序列。

console.log(...[1, 2, 3]);   // 1 2 3 
console.log(1, ...[2,3], 4); // 1 2 3 4
複製程式碼

擴充運算子主要使用在函式呼叫。

function f (a, b){
    console.log(a, b);
}
f(...[1, 2]); // 1 2

function g (a, b, c, d, e){
    console.log(a, b, c, d, e);
}
g(0, ...[1, 2], 3, ...[4]); // 0 1 2 3 4
複製程式碼

若擴充運算子後面是個空陣列,則不產生效果

[...[], 1]; // [1]
複製程式碼

替代apply方法

// ES6之前
function f(a, b, c){...};
var a = [1, 2, 3];
f.apply(null, a);

// ES6之後
function f(a, b, c){...};
let a = [1, 2, 3];
f(...a);

// ES6之前
Math.max.apply(null, [3,2,6]);

// ES6之後
Math.max(...[3,2,6]);
複製程式碼

擴充運算子的運用

  • (1)複製陣列
    通常我們直接複製陣列時,只是淺拷貝,如果要實現深拷貝,可以使用擴充運算子。
// 通常情況 淺拷貝
let a1 = [1, 2];
let a2 = a1; 
a2[0] = 3;
console.log(a1,a2); // [3,2] [3,2]

// 擴充運算子 深拷貝
let a1 = [1, 2];
let a2 = [...a1];
// let [...a2] = a1; // 作用相同
a2[0] = 3;
console.log(a1,a2); // [1,2] [3,2]
複製程式碼
  • (2)合併陣列
    注意,這裡合併陣列,只是淺拷貝。
let a1 = [1,2];
let a2 = [3];
let a3 = [4,5];

// ES5 
let a4 = a1.concat(a2, a3);

// ES6
let a5 = [...a1, ...a2, ...a3];

a4[0] === a1[0]; // true
a5[0] === a1[0]; // true
複製程式碼
  • (3)與解構賦值結合
    與解構賦值結合生成陣列,但是使用擴充運算子需要放到引數最後一個,否則報錯。
let [a, ...b] = [1, 2, 3, 4]; 
// a => 1  b => [2,3,4]

let [a, ...b] = [];
// a => undefined b => []

let [a, ...b] = ["abc"];
// a => "abc"  b => []
複製程式碼

5.2 Array.from()

類陣列物件可遍歷的物件,轉換成真正的陣列。

// 類陣列物件
let a = {
    '0':'a',
    '1':'b',
    length:2
}
let arr = Array.from(a);

// 可遍歷的物件
let a = Array.from([1,2,3]);
let b = Array.from({length: 3});
let c = Array.from([1,2,3]).map(x => x * x);
let d = Array.from([1,2,3].map(x => x * x));
複製程式碼

5.3 Array.of()

將一組數值,轉換成陣列,彌補Array方法引數不同導致的差異。

Array.of(1,2,3);    // [1,2,3]
Array.of(1).length; // 1

Array();       // []
Array(2);      // [,] 1個引數時,為指定陣列長度
Array(1,2,3);  // [1,2,3] 多於2個引數,組成新陣列
複製程式碼

5.4 find()和findIndex()

find()方法用於找出第一個符合條件的陣列成員,引數為一個回撥函式,所有成員依次執行該回撥函式,返回第一個返回值為true的成員,如果沒有一個符合則返回undefined

[1,2,3,4,5].find( a => a < 3 ); // 1
複製程式碼

回撥函式接收三個引數,當前值、當前位置和原陣列。

[1,2,3,4,5].find((value, index, arr) => {
    // ...
});
複製程式碼

findIndex()方法與find()類似,返回第一個符合條件的陣列成員的位置,如果都不符合則返回-1

[1,2,3,4].findIndex((v,i,a)=>{
    return v>2;
}); // 2
複製程式碼

5.5 fill()

用於用指定值填充一個陣列,通常用來初始化空陣列,並抹去陣列中已有的元素。

new Array(3).fill('a');   // ['a','a','a']
[1,2,3].fill('a');        // ['a','a','a']
複製程式碼

並且fill()的第二個和第三個引數指定填充的起始位置結束位置

[1,2,3].fill('a',1,2);//  [1, "a", 3]
複製程式碼

5.6 entries(),keys(),values()

主要用於遍歷陣列,entries()對鍵值對遍歷,keys()對鍵名遍歷,values()對鍵值遍歷。

for (let i of ['a', 'b'].keys()){
    console.log(i)
}
// 0
// 1

for (let e of ['a', 'b'].values()){
    console.log(e)
}
// 'a'
// 'b'

for (let e of ['a', 'b'].entries()){
    console.log(e)
}
// 0 'a'
// 1 'b'
複製程式碼

5.7 includes()

用於表示陣列是否包含給定的值,與字串的includes方法類似。

[1,2,3].includes(2);     // true
[1,2,3].includes(4);     // false
[1,2,NaN].includes(NaN); // true
複製程式碼

第二個引數為起始位置,預設為0,如果負數,則表示倒數的位置,如果大於陣列長度,則重置為0開始。

[1,2,3].includes(3,3);    // false
[1,2,3].includes(3,4);    // false
[1,2,3].includes(3,-1);   // true
[1,2,3].includes(3,-4);   // true
複製程式碼

5.8 flat(),flatMap()

flat()用於將陣列一維化,返回一個新陣列,不影響原陣列。
預設一次只一維化一層陣列,若需多層,則傳入一個整數引數指定層數。
若要一維化所有層的陣列,則傳入Infinity作為引數。

[1, 2, [2,3]].flat();        // [1,2,2,3]
[1,2,[3,[4,[5,6]]]].flat(3); // [1,2,3,4,5,6]
[1,2,[3,[4,[5,6]]]].flat('Infinity'); // [1,2,3,4,5,6]
複製程式碼

flatMap()是將原陣列每個物件先執行一個函式,在對返回值組成的陣列執行flat()方法,返回一個新陣列,不改變原陣列。
flatMap()只能展開一層。

[2, 3, 4].flatMap((x) => [x, x * 2]); 
// [2, 4, 3, 6, 4, 8] 
複製程式碼

6. 陣列的擴充(ES7)

6.1 Array.prototype.includes()方法

includes()用於查詢一個值是否在陣列中,如果在返回true,否則返回false

['a', 'b', 'c'].includes('a');     // true
['a', 'b', 'c'].includes('d');     // false
複製程式碼

includes()方法接收兩個引數,搜尋的內容開始搜尋的索引,預設值為0,若搜尋值在陣列中則返回true否則返回false

['a', 'b', 'c', 'd'].includes('b');      // true
['a', 'b', 'c', 'd'].includes('b', 1);   // true
['a', 'b', 'c', 'd'].includes('b', 2);   // false
複製程式碼

indexOf方法對比,下面方法效果相同:

['a', 'b', 'c', 'd'].indexOf('b') > -1;  // true
['a', 'b', 'c', 'd'].includes('b');      // true 
複製程式碼

includes()與indexOf對比:

  • includes相比indexOf更具語義化,includes返回的是是否存在的具體結果,值為布林值,而indexOf返回的是搜尋值的下標。
  • includes相比indexOf更準確,includes認為兩個NaN相等,而indexOf不會。
let a = [1, NaN, 3];
a.indexOf(NaN);     // -1
a.includes(NaN);    // true
複製程式碼

另外在判斷+0-0時,includesindexOf的返回相同。

[1, +0, 3, 4].includes(-0);   // true
[1, +0, 3, 4].indexOf(-0);    // 1
複製程式碼

參考資料

1.MDN 索引集合類
2.MDN 陣列物件
3.W3school JavaScript Array 物件


本部分內容到這結束

Author 王平安
E-mail pingan8787@qq.com
博 客 www.pingan8787.com
微 信 pingan8787
每日文章推薦 github.com/pingan8787/…
JS小冊 js.pingan8787.com

歡迎關注我的微信公眾號【前端自習課】

相關文章