寄語:
JS陣列的方法非常的豐富和瑣碎,學習和使用過程中總是需要反覆的記憶、檢視文件,但作為JS基礎這樣真的不應該,我們應該把它記得滾瓜爛熟,深入理解才對。
但是要怎樣才能做到呢?這正是我也本文的初衷,根據個人的經驗總結了一下,希望能夠幫助和我一樣困惑的人,搞定這個頭疼的問題。
陣列的建立
陣列的建立有三種方式:建構函式方式、字面量方式、ES6新增的Array.of()方法建立。
-
建構函式方式:
let arr = new Array(); // 建立一個空陣列 let arr = new Array(10); // 建立長度為10的陣列 let arr = new Array('a'); // 建立包含一個字串元素a的陣列 let arr = new Array(10, 'a'); // 建立包含10和字串a兩個元素的陣列 複製程式碼
小結:
1.new 關鍵字是可省略的
2.當只傳一個number型別引數時,建立的是引數指定長度的陣列。即建構函式形式建立陣列,不能建立只包含一個number型別元素的陣列 -
字面量方式:
let arr = [10, 'a']; // 字面量方式,即直接給陣列賦值 複製程式碼
-
Array.of()方法(ES6新增):
方法的作用是將一組值(即傳進來的引數)轉換為陣列。
這個方法彌補了建構函式建立陣列的不足。可以實現建立只有一個number型別元素的陣列。let arr = Array.of(3); console.log(arr); // [3] // 引數可以是各種型別 let arr1 = Array.of(1, 'a', true, null, undefined, {name: "zhangsan"}, [45]); console.log(arr1); // [ 1, 'a', true, null, undefined, { name: 'zhangsan' }, [ 45 ] ] 複製程式碼
陣列的檢測
兩種較為準確的檢測方法:
- 利用物件的toString方法:
Object.prototype.toString.call([]) === "[object Array]"; // true - Array.isArray():
Array.isArray([1, 2, 3]); // true
陣列的屬性
- length屬性:
作用:- 設定或返回陣列的長度
- 可用來增加和刪除陣列項
let arr = [1, 2]; arr.length = 1; console.log(arr); // [1] arr.length = 3; console.log(arr); // [1, , ] 複製程式碼
陣列的方法
JavaScript的陣列方法包括陣列原型的方法、建構函式的方法(ES6新增部分)
針對每一個方法我們主要了解四個方面:作用、引數、返回值、原陣列是否改變
(一). 原型上的方法
-
push():
作用:向陣列的末尾新增一項或多項
引數:ele1[, ele2[, ...[, elen]]]
返回值:新增元素後陣列的長度
原陣列是否改變:是let arr = [1, 2, 3]; let temp = arr.push('a', 'b'); console.log(arr, temp); // [1, 2, 3, 'a', 'b'] 5 複製程式碼
-
pop():
作用:刪除陣列最後一項
引數:無
返回值:刪除的那一項
原陣列是否改變:是let arr = [1, 2, 3]; let temp = arr.pop(); console.log(arr, temp); // [1, 2] 3 複製程式碼
-
unshift():
作用:向陣列開頭新增一項或多項
引數:ele1[, ele2[, ...[, elen]]]
返回值:新增元素後陣列的長度
原陣列是否改變:是let arr = [1, 2, 3]; let temp = arr.unshift('a', 'b'); console.log(arr, temp); // ['a', 'b', 1, 2, 3] 5 複製程式碼
-
shift():
作用:刪除陣列第一項
引數:無
返回值:刪除的那一項
原陣列是否改變:是let arr = [1, 2, 3]; let temp = arr.shift(); console.log(arr, temp); // [2, 3] 1 複製程式碼
-
splice():
作用:刪除、插入、替換陣列項
引數:startIndex[, deleteCount[, item1[, ...[, itemN]]]]
返回值:刪除項組成的陣列
原陣列是否改變:是let arr = [1, 2, 3]; // 插入元素 let temp = arr.splice(1, 0, 'a', 'b'); // 在索引1的位置插入元素'a'和'b' console.log(arr, temp); // [1, 'a', 'b', 2, 3] [] // 刪除元素 let temp1 = arr.splice(1, 2); // 刪除從索引1的位置開始的2項 console.log(arr, temp1); // [1, 2, 3] ['a', 'b'] // 替換一個元素 let temp2 = arr.splice(1, 1, 'a'); // 將索引1的位置的元素替換為'a' console.log(arr, temp2); // [1, 'a', 3 ] [2] // 替換多個元素 let temp3 = arr.splice(0, 2, 'b', 'c'); // 將索引0的位置開始的兩項,替換成’b‘和’c‘ console.log(arr, temp3); // [’b‘, 'c', 3] [1, 'a'] // 只傳第一個引數,則刪除從第一個引數指定的位置到陣列結尾的所有項 let temp4 = arr.splice(0); //從索引0的位置開始,刪除後面的所有項 console.log(arr, temp4); // [] [’b‘, 'c', 3] 複製程式碼
-
copyWithin()
作用:將陣列指定位置(start到end)的元素複製到當前陣列的其他位置(target開始),這種複製會替換原位置的元素(ES6新增)
引數說明:target[,start[,end]]
引數說明:- target: 複製的目標位置(包括),即要被替換的元素開始的位置
- start: 要copy的元素的開始位置,預設0
- end: 要copy的元素的結束位置,預設為陣列最後一個元素
返回值:複製替換之後的陣列
原陣列是否改變:是let arr = [1, 2, 3, 4, 5]; // 用索引0~4範圍內的元素,替換索引3~4範圍內的元素,因為要替換的位置只有兩個,所以只將4,5替換為了1,2 let temp = arr.copyWithin(3); console.log(arr, temp); // [1, 2, 3, 1, 2] [1, 2, 3, 1, 2] let arr1 = [1, 2, 3, 4, 5]; // 用索引2~4範圍內的元素,替換索引3~4範圍內的元素,因為要替換的位置只有兩個,所以只將4,5替換為了3,4 let temp1 = arr1.copyWithin(3, 2); console.log(arr1, temp1); // [1, 2, 3, 3, 4] [1, 2, 3, 3, 4] 複製程式碼
小結:
總結上述的描述,copyWithin的作用就是在陣列長度的範圍內,複製start(包括)到end(不包括)範圍內的元素,然後用上述的元替換掉從target(包括)開始到陣列結尾的元素,能替換多少就替換多少 -
reverse():
作用:翻轉原陣列
引數:無
返回值:翻轉後的陣列
原陣列是否改變:是let arr = [1, 2, 3]; let temp = arr.reverse(); console.log(arr, temp); // [ 3, 2, 1 ] [ 3, 2, 1 ] 複製程式碼
-
sort():
作用:陣列排序
引數:compareFunction
引數說明:- compareFunction返回值大於0時調換當前比對項的順序,否則順序不 變;
- 引數可以不傳,不傳預設按照Unicode編碼的順序排列
返回值:排序後的陣列
原陣列是否改變:是// 陣列從小到大排序 let arr = [1, 4, 6, 7, 8, 3, 2]; let temp = arr.sort((a, b) => { return a - b; }) console.log(arr, temp); // [ 1, 2, 3, 4, 6, 7, 8 ] [ 1, 2, 3, 4, 6, 7, 8 ] // 一個實用的陣列排序的例子,根據物件元素的排序,排序物件在陣列中的位置 let objArr = [{id: 3, name: "lilei"},{id: 1, name: "hanmeimei"},{id: 2, name: "yimi"}]; let tempArr = objArr.sort((a, b) => { // 按照id從小到大的順序,對陣列中的物件進行排序 // 這個示例說明回撥函式的形參a,b實際就是陣列中當前進行比對的兩個元素 return a.id - b.id; }); console.log(objArr); // [{id: 1, name: 'hanmeimei'}, {id: 2, name: 'yimi'}, { id: 3, name: 'lilei' }] console.log(tempArr); // [{id: 1, name: 'hanmeimei'}, {id: 2, name: 'yimi'}, { id: 3, name: 'lilei'}] 複製程式碼
-
concat():
作用:基於當前的陣列拼接陣列
引數:value1[, value2[, ...[, valueN]]
引數說明:- 引數的型別可以是任意型別。
- 不是陣列型別直接按順序拼接到陣列末尾,陣列型別的則將陣列元素逐一取出拼接到陣列末尾
- 不傳則相當於複製陣列
返回值:拼接後的陣列
原陣列是否改變:否let arr = [1,2]; let temp = arr.concat('a', {id:1}, ['lilei', 'hanmeimei']); console.log(arr, temp); // [ 1, 2 ] [ 1, 2, 'a', { id: 1 }, 'lilei', 'hanmeimei'] // 用於複製陣列 let arr = [1, 2]; let temp = arr.concat(); console.log(arr, temp); // [ 1, 2 ] [ 1, 2 ] 複製程式碼
-
slice():
作用:基於當前陣列的一項或多項建立一個新的陣列
引數:startIndex[,endIndex]
引數說明:返回的元素包含startIndex位置的元素,但不包括endIndex位置的元素
返回值:返回擷取的元素組成的陣列
原陣列是否改變:否let arr = [0, 1, 2, 3, 4]; let temp = arr.slice(1,3); // 返回從索引1(包括)位置到索引3(不包括)位置之前的元素 console.log(arr, temp); // [0, 1, 2, 3, 4] [1, 2] // 用於複製陣列 let arr = [0, 1, 2, 3, 4]; let temp = arr.slice(0); // 返回從索引0(包括)位置到陣列結尾的所有元素 console.log(arr, temp); // [0, 1, 2, 3, 4] [0, 1, 2, 3, 4] 複製程式碼
-
indexOf():
作用:從陣列開頭查詢元素在陣列中的索引位置(ES5的方法)
引數:searchElement[, fromIndex]
返回值:searchElement在陣列中的索引,沒找到searchElement則返回-1
原陣列是否改變:否let arr = [1, 2, 3, 4, 5, 6, 2]; // 從陣列開頭開始查詢 let temp = arr.indexOf(2); console.log(arr, temp); // [ 1, 2, 3, 4, 5, 6, 2 ] 1 // 從指定的位置開始查詢 let temp1 = arr.indexOf(2,3); // 從索引3(包括)的位置向後查詢元素2 console.log(arr, temp1); // [ 1, 2, 3, 4, 5, 6, 2 ] 6 複製程式碼
-
lastIndexOf():
作用:從陣列結尾查詢元素在陣列中的索引位置(ES5的方法)
引數:searchElement[, fromIndex]
返回值:searchElement在陣列中的索引,沒找到searchElement則返回-1 原陣列是否改變:否let arr = [1, 2, 3, 4, 5, 6, 2]; // 從陣列末尾開始查詢 let temp = arr.lastIndexOf(2); console.log(arr, temp); // [ 1, 2, 3, 4, 5, 6, 2 ] 6 // 從指定的位置開始查詢 let temp1 = arr.lastIndexOf(2,3); // 從索引3(包括)的位置向前查詢元素2 console.log(arr, temp1); // [ 1, 2, 3, 4, 5, 6, 2 ] 1 複製程式碼
-
every():
作用:對陣列中的每一項執行給定函式,如果該函式對每一項都返回true,則返回true(ES5方法)
引數:callback[, thisArg]
引數說明:callback有三個引數item(當前項),index(當前項索引),array(陣列物件本身)
返回值:true 或 false
原陣列是否改變:涉及callback,因此不確定,具體詳情見下文中的原型方法的小結部分。let arr = [1, 2, 3, 4]; let temp = arr.every((item, index, array) => { return item > 2; }); console.log(arr, temp); // [ 1, 2, 3, 4 ] false // 方法的第二個引數可選,作用是設定第一個引數中的this指向,如果使用第二個引數,注意callback不能是箭頭函式 // 後面的迭代方法涉及此引數的,用法相同,不在贅述 let arr = [1, 2, 3, 4]; let temp = arr.every(function(item, index, array) { return item > this.id; }, {id: 2}); console.log(arr, temp); // [ 1, 2, 3, 4 ] false 複製程式碼
-
some():
作用:對陣列中的每一項執行給定函式,如果該函式對任意一項返回true,則返回true(ES5方法)
引數:callback[, thisArg]
引數說明:callback有三個引數item(當前項),index(當前項索引),array(陣列物件本身)
返回值:true 或 false
原陣列是否改變:涉及callback,因此不確定,具體詳情見下文中的原型方法的小結部分。let arr = [1, 2, 3, 4]; let temp = arr.some((item, index, array) => { return item > 2; }); console.log(arr, temp); // [ 1, 2, 3, 4 ] true 複製程式碼
-
filter():
作用:對陣列中的每一項執行給定函式,返回該函式返回true的項組成的陣列(ES5方法)
引數:callback[, thisArg]
引數說明:callback有三個引數item(當前項),index(當前項索引),array(陣列物件本身)
返回值:函式返回true的項組成的陣列
原陣列是否改變:涉及callback,因此不確定,具體詳情見下文中的原型方法的小結部分。let arr = [1, 2, 3, 4]; let temp = arr.filter((item, index, array) => { return item > 2; }); console.log(arr, temp); // [ 1, 2, 3, 4 ] [3, 4] 複製程式碼
-
map():
作用:對陣列中的每一項執行給定函式,返回每次函式呼叫的結果組成的陣列(ES5方法)
引數:callback[, thisArg]
引數說明:callback有三個引數item(當前項),index(當前項索引),array(陣列物件本身)
返回值:函式每次呼叫結果組成的陣列
原陣列是否改變:涉及callback,因此不確定,具體詳情見下文中的原型方法的小結部分。let arr = [1, 2, 3, 4]; let temp = arr.map((item, index, array) => { return item * item; }); console.log(arr, temp); // [ 1, 2, 3, 4 ] [ 1, 4, 9, 16] 複製程式碼
-
forEach():
作用:對陣列中的每一項執行給定函式。無返回值(ES5方法)
引數:callback[, thisArg]
引數說明:callback有三個引數item(當前項),index(當前項索引),array(陣列物件本身)
返回值:無
原陣列是否改變:涉及callback,因此不確定,具體詳情見下文中的原型方法的小結部分。let arr = [1, 2, 3, 4]; let temp = arr.forEach((item, index, array) => { // 不會有返回值,但可在這裡執行某些操作 return item * item; }); console.log(arr, temp); // [ 1, 2, 3, 4 ] undefined 複製程式碼
注意:
forEach在所有項都遍歷完成之前,無法像for迴圈一樣提前終止迴圈 -
reduce():
作用:從陣列的第一項開始,逐步遍歷到最後,迭代陣列的所有項(ES5方法)
引數:callback[, initialValue]
引數說明:- callback迭代函式,有四個引數(prev, cur, index, array)
- prev 前一個值,(initialValue || 陣列第一項 || 上一次迭代的結果)
- cur 當前迭代項
- index 當前迭代項索引
- array 迭代的原陣列
- initialValue 迭代的基礎值,不傳基礎值是陣列第一項
返回值:陣列迭代後,整體的迭代結果
原陣列是否改變:涉及callback,因此不確定,具體詳情見下文中的原型方法的小結部分。// 陣列求和 let arr = [1, 2, 3]; let sum = arr.reduce((prev, cur, index, array) => { return prev + cur; }); console.log(arr, sum); // [ 1, 2, 3 ] 6 // 傳initialValue 基礎值的示例 let sum1 = arr.reduce((prev, cur, index, array) => { return prev + cur; }, 10); // 返回的值是:10+1+2+3 console.log(arr, sum1); // [ 1, 2, 3 ] 16 複製程式碼
reduce原始碼的實現:
Array.prototype.myReduce = function(callback, initialVal){ let prev = initialVal || this[0]; for(var i = pre ? 0 : 1; i < this.length; i++){ prev = callback(prev, this[i], i, this); } return prev } 複製程式碼
- callback迭代函式,有四個引數(prev, cur, index, array)
-
reduceRight():
作用:從陣列的最後一項開始,逐步遍歷到第一項,迭代陣列的所有項(ES5方法)
引數:callback[, initialValue]
引數說明:- callback迭代函式,有四個引數(prev, cur, index, array)
- prev 前一個值,(initialValue || 陣列第一項 || 上一次迭代的結果)
- cur 當前迭代項
- index 當前迭代項索引
- array 迭代的原陣列
- initialValue 迭代的基礎值,不傳基礎值是陣列第一項
返回值:陣列迭代後,整體的迭代結果
原陣列是否改變:涉及callback,因此不確定,具體詳情見下文中的原型方法的小結部分。// 拼接字串,從後向前迭代陣列進行拼接 let arr = ['h', 'e', 'l', 'l', 'o']; let str = arr.reduceRight((prev, cur, index, array) => { return prev + cur; }); console.log(arr, str); // [ 'h', 'e', 'l', 'l', 'o' ] 'olleh' 複製程式碼
- callback迭代函式,有四個引數(prev, cur, index, array)
-
find():
作用:查詢陣列中第一個符合條件的元素,返回該元素 (ES6新增)
引數:callback[, thisArg]
引數說明:引數的使用同上述的forEach、every、map、some、filter方法一致
返回值:查詢到則返回該元素,沒找到返回undefined
原陣列是否改變:涉及callback,因此不確定,具體詳情見下文中的原型方法的小結部分。let arr = [1, 2, 3, 4, 5]; let temp = arr.find((item, index, array) => { return item > 2; }) console.log(arr, temp); // [1, 2, 3, 4, 5] 3 複製程式碼
-
findIndex():
作用:查詢陣列中第一個符合條件的元素所在位置的索引,並返回該索引值
引數:callback[, thisArg]
引數說明:引數的使用同上述的forEach、every、map、some、filter方法一致
返回值:查詢到則返回該索引值,沒找到返回-1
原陣列是否改變:涉及callback,因此不確定,具體詳情見下文中的原型方法的小結部分。let arr = [1, 2, 3, 4, 5]; let temp = arr.findIndex((item, index, array) => { return item > 2; }) console.log(arr, temp); // [1, 2, 3, 4, 5] 2 複製程式碼
-
fill():
作用:用指定元素,填充陣列從start(包括)到end(不包括)之間的元素,如果該區間內已經有元素,直接替換掉(ES6新增)
引數:value[, start[, end]]
返回值:填充後的陣列
原陣列是否改變:是let arr = [1, 2, 3, 4, 5]; let temp = arr.fill('a', 2, 4); console.log(arr, temp); // [1, 2, 'a', 'a', 5] [1, 2, 'a', 'a', 5] 複製程式碼
-
includes():
作用:判斷陣列中是否包含指定的元素(ES7新增)
引數:searchElement[, fromIndex]
返回值:true或false
原陣列是否改變:否let arr = [1, 2, 3, 4, 5]; let temp = arr.includes(5); console.log(arr, temp); // [1, 2, 3, 4, 5] true // 這個方法彌補了indexOf檢視元素時的一個不足,即查詢NaN的誤差 let arr1 = [NaN, 'a']; let temp1 = arr1.includes(NaN); let temp2 = arr1.indexOf(NaN); console.log(temp1, temp2); // true -1 複製程式碼
-
toString()、toLocalString():
作用:呼叫陣列每一項的toString()方法,返回的是以逗號分隔的字串
引數:無
返回值:轉化後的字串
原字陣列是否改變:否let arr = [1, [1, 2, [4]], {name: "zhangsan"}, 3]; let temp = arr.toString(); console.log(arr); [ 1, [ 1, 2, [ 4 ] ], { name: 'zhangsan' }, 3 ] console.log(temp); // '1,1,2,4,[object Object],3' 複製程式碼
-
join():
作用:將陣列元素轉化為字串(呼叫每個元素的toString方法),並使用指定的分隔符(預設為逗號)進行拼接,返回拼接後的字串
引數:分隔符,預設為逗號(,)
返回值:拼接後的字串
原陣列是否改變:否let arr = [1, [1, 2, [4]], {name: "zhangsan"}, 3]; let temp = arr.join(); console.log(arr); [ 1, [ 1, 2, [ 4 ] ], { name: 'zhangsan' }, 3 ] console.log(temp); // '1,1,2,4,[object Object],3' // 陣列求和 let arr1 = [1, 2, 3]; console.log(eval(arr1.join('+'))); // 6 複製程式碼
原型方法的總結:
1. 陣列的方法無外乎是對陣列的增刪改查、轉換、迭代。增、刪、改都會改變原有的陣列,查、轉換的方法不涉及callback引數的不會改變原陣列,涉及到的則視情況而定,迭代方法因為均涉及到callback引數,因此也不確定。
那麼為什麼涉及到callback就不確定了呢???
首先如果直接在callback中操作原陣列,那肯定原陣列會改變。例如:
let arr = [1,2,3,4]; let temp = arr.forEach((item,index,array) => { // 直接通過索引操作原陣列 array[index] *= item; }); console.log(arr,temp); // [1, 4, 9, 16] undefined 複製程式碼
如果不是直接操作原陣列,而是操作callback的item引數的時,如果item是基本資料型別則原陣列中對應的該項元素不會改變,如果是引用型別(陣列,物件、函式等)則改變,因為操作引用型別的值,實質是操作該值所在存貯地址的內容,而item對應的原陣列中的元素和item是同一引用地址,因此會導致原陣列中對應元素改變。(小夥伴們對這裡如果還是不理解,可以看下陣列方法polyfill的實現,這裡不再贅述)
2. 所有涉及索引的方法,開始位置都是在操作範疇的,結束位置都是不包括在操作範圍內的
(二).建構函式的方法
-
Array.from():
作用:將類陣列轉化為陣列
引數:arrayLike[, mapFn[, thisArg]]
引數說明:- arrayLike:類陣列物件,可以是我們常見的nodeList、arguments、字串、iterable物件等
- mapFn: 對轉化後的陣列進行操作的回撥函式
- thisArg: 指定mapFun中的this
返回值:轉化後的陣列,如果有mapFn,則返回結果是經過mapFn處理的陣列
原類陣列是否改變:不使用mapFn,則類陣列不改變。使用mapFn則結果同上述迭代方法中使用callback的情況一致。let str = 'hello'; let temp = Array.from(str); console.log(str, temp); // hello [ 'h', 'e', 'l', 'l', 'o' ] let temp1 = Array.from(str, (item, index) => { return item.toUpperCase(); }); console.log(str, temp1); // hello [ 'H', 'E', 'L', 'L', 'O' ] 複製程式碼
小結:
Array.from() 等價於 Array.prototype.slice.call(arguments,0)
陣列擴充套件運算子(ES6新增)
陣列的擴充套件運算子可以將陣列轉化為以逗號分割的引數序列。
幾個簡單使用的應用場景:
- 將陣列通過擴充套件運算子轉化為引數序列直接傳參,無需使用apply轉化了
let arr = [1, 2, 3]; // apply寫法 Math.min.apply(null, arr) // 擴充套件運算子寫法 Math.min(...arr) 複製程式碼
- 可以用於複製和拼接陣列
let arr1 = [2, 3, 4]; let arr2 = ['a', 'b', 'c']; // 拼接陣列arr1和arr2 console.log([...arr1, ...arr2]); // [2, 3, 4, 'a', 'b', 'c'] 複製程式碼
- 可用於將字串分解為真正的陣列,
[…'hello'] // [ 'h', 'e', 'l', 'l', 'o' ] 複製程式碼
寫在最後
長篇大論了這麼久,不是為了重複書中或者官方的使用說明。而是再分享一些學習的思路,讓初學者能夠更快更準的掌握JavaScript陣列,讓停留在會用層次的同學瞭解它更多。
希望我上述的方法說明能夠讓你對JavaScript陣列一目瞭然,在各個方法所闡述的幾個方面的對比中,將它更輕易的記住。