Array API 數組方法在JS中是一個重要的知識點。本文將重點講解一些方法的使用細節和使用場景。以下是對數組方法的分類 靜態方法 | 方法 | 說明 | | | | | Array.from() | 從數組類對象或可迭代對象創建一個新的 Array 實例。 | | Array.isArray() ...
目錄
Array API
數組方法在JS中是一個重要的知識點。本文將重點講解一些方法的使用細節和使用場景。以下是對數組方法的分類
靜態方法
方法 | 說明 |
---|---|
Array.from() |
從數組類對象或可迭代對象創建一個新的 Array 實例。 |
Array.isArray() |
如果參數是數組則返回 true ,否則返回 false 。 |
應用場景:Array.from()
這個方法常用於轉化NodeList
類數組對象,讓DOM集合也能擁有數組方法。
數組首尾元素處理
方法 | 說明 |
---|---|
Array.prototype.pop() |
從數組中移除最後一個元素並返回該元素。 |
Array.prototype.push() |
在數組末尾添加一個或多個元素,並返回數組新的 length 。 |
Array.prototype.shift() |
從數組中移除第一個元素並返回該元素。 |
Array.prototype.unshift() |
在數組的前面添加一個或多個元素,並返回數組新的 length 。 |
應用場景:我們可以利用上述方法能夠非常簡單的模擬棧和隊列這兩種數據結構,如下:
let arr = [ 1, 2, 3, 4, 5 ]
// 棧數據結構,後進先出。
arr.pop(5) // 出棧操作 [ 1, 2, 3, 4 ]
arr.push(5) // 入棧操作 [ 1, 2, 3, 4, 5 ]
// 隊列數據結構,先進先出。
arr.shift(1) // 出隊操作 [ 2, 3, 4, 5 ]
arr.push(1) // 入隊操作 [ 1, 2, 3, 4, 5 ]
其中pop
和push
方法分別對應棧的出棧與入棧操作。shift
和push
方法分別對應隊列的出隊和入隊操作。
當然,我們也可以進一步基於以上方法利用面向對象編程分別封裝棧和隊列這樣的數據結構進行操作。
數組遍歷(重要)
方法 | 說明 |
---|---|
Array.prototype.forEach() |
對調用數組中的每個元素調用函數。 |
Array.prototype.map() |
返回一個新數組,其中包含對調用數組中的每個元素調用函數的結果。 |
應用場景:以上兩個方法都十分常用。其中NodeList
自帶forEach
方法,在DOM編程中可以應用。 此外,在vue中我們常常會設計這樣一個數據結構用於v-for迴圈。
let arr = [
{ id: 0, name: "小紅", age: 22 },
{ id: 1, name: "小華", age: 18 },
{ id: 2, name: "小明", age: 24 },
]
arr.forEach((element) => {
console.log(element.name) // 結果:"小紅" "小明" "小華"
})
let result = arr.map((element) => { // 方法返回一個新數組,我們使用result變數接收
return element.id * 2 // 返回的element會作為result數組中的元素
})
console.log(result) // 結果:[ 0, 2, 4 ]
使用以上方法可以非常簡便的訪問數組中每個對象的屬性,以此對每個對象屬性值進行統計、運算和賦值等操作。
數組查找
方法 | 說明 |
---|---|
Array.prototype.find() |
返回數組中滿足提供的測試函數的第一個元素的值,如果沒有找到合適的元素,則返回 undefined 。 |
Array.prototype.findIndex() |
返回數組中滿足提供的測試函數的第一個元素的索引,如果沒有找到合適的元素,則返回 -1 。 |
Array.prototype.indexOf() |
返回在調用數組中可以找到給定元素的第一個(最小)索引。 |
應用場景:需要根據條件獲取索引或值的元素。一般根據返回值用於判斷數組內是否存在符合條件的元素。
數組過濾(重要)
方法 | 說明 |
---|---|
Array.prototype.includes() |
確定調用數組是否包含一個值,根據情況返回 true 或 false 。 |
Array.prototype.some() |
如果調用數組中至少有一個元素滿足提供的測試函數,則返回 true 。 |
Array.prototype.every() |
如果調用數組中的每個元素都滿足測試函數,則返回 true 。 |
Array.prototype.filter() |
返回一個新數組,其中包含調用所提供的篩選函數返回為 true 的所有數組元素。 |
Array.prototype.reduce() |
對數組的每個元素(從左到右)執行用戶提供的 “reducer” 回調函數,將其簡化為單個值。 |
應用場景:這裡重點講解filter
和reduce
兩個最常用的方法。
reduce
方法應用廣泛,可用於數組去重、數組扁平化等操作。基本使用方法如下:
reduce((previousValue, currentValue) => { /* … */ } , initialValue)
其中reduce
方法接收兩個參數:一個是回調函數,一個是初始值。若已設定初始值initialValue
,回調函數中的第一個參數previousValue
等於initialValue
,第二個參數為數組的第一個元素。在reduce
方法遍歷過程中,回調函數的第二個參數currentValue
將會按順序訪問數組元素。第一個參數previousValue
則為上一次return
的結果。來看一個數組去重的應用例子:
let a = [1, 2, 2, 3, 3, 3]
function unique(arr) {
return arr.reduce((pre, cur) => {
return pre.includes(cur) === true ? pre : pre.concat(cur) // concat會返回一個新數組
// 判斷上一次return的結果(pre)是否存在相同元素。
// 如果存在pre不變直接return返回;若不存在pre合併當前元素後return返回
}, []) // [] 空數組作為初始值
}
console.log(unique(a)) // 結果:[ 1,2,3 ]
filter
方法主要應用於有條件需要進行數據篩選的數組。還是上上面的數組結構,如下:
let arr = [
{ id: 0, name: "小紅", age: 22 },
{ id: 1, name: "小華", age: 18 },
{ id: 2, name: "小明", age: 24 },
]
// 這裡做一個篩選,我們只要大於18歲的人作為新數組數據
// 使用filter方法
let filterResult = arr.filter(element => element.age > 18) // 不用花括弧會直接return表達式
console.log(filterResult)
// 結果:[{ id: 0, name: "小紅", age: 22 }, { id: 2, name: "小明", age: 24 }]
// 使用map方法
let mapResult = arr.map(element => element.age > 18) // 不用花括弧會直接return表達式
console.log(mapResult)
// 結果:[ true, false, true ]
這裡做一個比較:filter
相比map
的區別。當數組內元素的不符合條件時,map
方法內部不符合條件的元素仍然會根據return
的內容占據原來的位置;而filte
r方法則根據return
的條件(true
或false
)去掉不符合的元素,返回符合條件元素組成的數組。
數組合併
方法 | 說明 |
---|---|
Array.prototype.join() |
將數組的所有元素連接為字元串。 |
Array.prototype.concat() |
返回一個新數組,該數組由被調用的數組與其它數組或值連接形成。 |
說明:concat
方法可以進行數組合併。如下:
let arr = [].concat([1, 2], [3, 4])
console.log(arr)
// 輸出結果:[1, 2, 3, 4]
值得註意的點:
-
concat
方法不會改變數組,而是返回一個新數組,需要使用變數接收這個新數組。 -
concat
方法傳入的參數如果是嵌套數組會展開其第一層進行合併。如下:
let arr = [].concat(1, 2, [3, 4, [5, 6, [7, 8]]])
console.log(arr)
// 輸出結果:[1, 2, 3, 4, Array(3)]
應用場景:我們利用上面concat
方法傳入參數是嵌套數組會展開一層進行合併的特點,再加上擴展運算符和遞歸的思想可以非常巧妙的實現數組扁平化。如下:
// 三行代碼實現數組扁平化
function flatten(arr, deep) {
return deep > 0 ? flatten([].concat(...arr), deep - 1) : arr
}
// 參數說明:arr = 原數組,deep = 展開的深度(從0開始,完全展開可以設為Infinity)
let a = [1, 2, [3, 4, [5, 6, [7, 8]]]] // 原數組
let result = flatten(a, 3) // 傳入數組執行方法,展開深度為3
console.log(result)
// 輸出結果:[1, 2, 3, 4, 5, 6, 7, 8]
過程分析如下:
原始數組:[1, 2, [3, 4, [5, 6, [7, 8]]]]
擴展運算符將原始數組展開後:1, 2, [3, 4, [5, 6, [7, 8]]]
concat
方法的傳入參數合併後:[1, 2, 3, 4, [5, 6, [7, 8]]]
重覆上面的展開與合併過程,可以深度拍平數組。
數組刪除與截取
方法 | 說明 |
---|---|
Array.prototype.slice() |
提取調用數組的一部分並返回一個新數組。 |
Array.prototype.splice() |
從數組中添加和/或刪除元素。 |
說明:我們重點關註slice
和splice
的參數說明。如下:
let a = [1, 2, 3, 4, 5, 6]
let b = a.slice(2, 4) // 提取位置2到位置4的元素,返回一個新數組用變數b接收
console.log(b) // [3, 4]
參數說明:slice(start, end)
方法可接收兩個參數,一般用於提取數組中的元素。
-
start
是數組開始提取的位置,若是負值則代表倒數第幾位開始(等價array.length - n
),若start
超出數組長度則會返回空數組。 -
end
代表數組終止提取的位置,若end
被省略或end
超出數組長度slice()
方法都會從start
開始提取到數組末尾。 -
slice()
會提取原數組中索引從begin
到end
的所有元素(包含begin
,但不包含end
)作為新數組返回。
let a = [1, 2, 3, 4, 5, 6]
a.splice(2, 4, 7) // 從位置2開始刪除4個元素,刪除後在原數組位置2添加元素7
console.log(a) // [1,2, 7]
參數說明:splice(start, deleteCount, item1, item2, itemN)
方法可接收三個以上參數,可用於刪除數組中的元素,
-
start
:代表數組開始修改的位置,若是負值則代表倒數第幾位開始(等價array.length - n
),若start
超出數組長度則以末尾開始修改。 -
deleteCount
:代表從start
開始要移除的元素個數(含第start
位), 若deleteCount
大於start
之後的元素的總數,則從start
後面的元素都將被刪除。 -
item1,item2...
:代表刪除後從原數組start
位置開始要添加的數據,會在刪除後不指定則splice()
方法只會刪除數組元素。
數組排序
方法 | 說明 |
---|---|
Array.prototype.sort() |
對數組的元素進行排序並返回該數組。 |
Array.prototype.reverse() |
反轉數組中元素的順序。 |
sort
方法利用原地演算法對數組進行排序。簡單來說就是在不開闊新的空間情況下,只能在原數組內用相互替換的方法交換數組內元素進行排序。比如冒泡排序、插入排序和希爾排序等空間複雜度為O(log n)
的排序演算法。當然我們最關心的還是它的用法。sort
方法接收一個回調函數fn
,回調函數里可以傳入兩個參數a
和b
。排序過程中回調函數的返回值可以有以下情況:
-
fn(a, b) > 0
:a
在b
之後 -
fn(a, b) < 0
:a
在b
之前 -
fn(a, b) === 0
:a
和b
位置保持不變
let strs = ['Axios', 'Candy', 'Mike', 'Alice']
let a = strs.sort()// 沒有回調函數,預設按元素轉化成後字元串的各個字元的Unicode代碼點進行排序
console.log(a) // ['Alice', 'Axios', 'Candy', 'Mike']
// 字母相同的元素('Axios'和'Alice')會利用元素下一個字元串的Unicode代碼點繼續進行排序
let nums = [2, 1, 4, 3, 5, 6]
let b = nums.sort((a, b) => a - b) // 存在回調函數,a-b為升序
console.log(b)
let c = nums.sort((a, b) => b - a) // 存在回調函數,b-a為降序
console.log(c)
let objs = [
{ id: 0, name: "小紅", age: 22 },
{ id: 1, name: "小華", age: 18 },
{ id: 2, name: "小明", age: 24 },
]
let d = objs.sort((a, b) => a.age - b.age) // 也可以利用對象屬性值進行排序(這裡是升序)
console.log(d) // 按年齡大小對元素排序
reverse()
方法用於數組逆序,如下:
let nums = [1, 2, 3, 4, 5, 6]
nums.reverse()
console.log(nums) // [6, 5, 4, 3, 2, 1]
特別註意:所有返回新數組的數組方法都不會改變原有數組,因此要用一個新變數接收返回的新數組。
返回新數組的方法總結如下:
Array.prototype.map()
Array.prototype.filter()
Array.prototype.concat()
Array.prototype.slice()
Array.prototype.sort
String API
在網路上我們每天都需要利用字元進行交流,間接說明我們應該掌握字元串的處理方式。這是前端開發中不可或缺的技能。以下是字元串方法的分類:
字元串查找與匹配
方法 | 說明 |
---|---|
String.prototype.indexOf() |
並返回指定子字元串第一次出現的索引。 |
String.prototype.match() |
用於將正則表達式 regexp 與字元串匹配。 |
String.prototype.search() |
搜索正則表達式 regexp 和調用字元串之間的匹配項。 |
我們重點關註一下indexOf
方法。indexOf
方法可以搜索子字元串第一次出現的索引。根據傳入參數的不同搜索的結果分別以下有三種情況:
let a = "abc";
a.indexOf("c") // 正確傳參,輸出結果:2
a.indexOf() // 不傳參數,輸出結果:"undefined"
a.indexOf("d") // 傳入參數錯誤或子字元串在字元串中找不到,輸出結果:-1
match
方法傳入一個正則表達式作為參數,與search
方法的區別是:match
方法會返回結果分為兩種情況:若正則表達式設為全局匹配g
會返回一個匹配的子字元串結果(數組形式),若未設定全局匹配則會返回一個有特定結構的結果數組。如果都沒匹配成功則返回null
。search
方法則會返迴首次匹配字元串的索引,不匹配返回-1
。
let a = "AbCdEfG"
let regex1 = /[A-Z]/g // 設定全局匹配
a.match(regex1) // 輸出結果:['A', 'C', 'E', 'G']
let regex2 = /[A-Z]/ // 為設定全局匹配
a.match(regex2) // 輸出結果:['A', index: 0, input: 'AbCdEfG', groups: undefined]
字元串替換
方法 | 說明 |
---|---|
String.prototype.replace() |
用於使用 replaceWith 替換出現的 searchFor 。 |
應用場景:可以做字元替換,replace
方法接收兩個參數。第一個參數可以是正則表達式或字元串,第二個參數是要替換的內容。replace
方法在字元串匹配成功後,會進行替換。替換如下:
let str = 'My name is Jelly';
str.replace('Jelly', 'Mary') // 輸出結果: 'My name is Mary'
字元串合併
方法 | 方法 |
---|---|
String.prototype.concat() |
合併兩個(或更多)字元串的文本並返回一個新字元串。 |
字元串的concat
方法跟數組的concat
方法操作基本相同。用於字元串的合併操作。
字元串首尾空格去除
方法 | 說明 |
---|---|
String.prototype.trim() |
修剪字元串開頭和結尾的空格。 |
String.prototype.trimStart() |
修剪字元串開頭的空格。 |
String.prototype.trimEnd() |
修剪字元串結尾的空格。 |
應用場景:去掉字元串的首尾空格,如下:
let str = ' Hello, World ';
str.trim(); // 輸出結果:'Hello, World'
字元串大小寫轉化
方法 | 說明 |
---|---|
String.prototype.toLowerCase() |
字元串中的字元將轉換為小寫。 |
String.prototype.toUpperCase() |
字元串中的字元將轉換為大寫。 |
應用場景:以上的字元串方法可以應用於字母的大小寫替換,可以來做一道題。實現小駝峰式命名法:將一個英語短語轉換為小駝峰式命名。例如:first name
=> firstName
function toConvertCase(str) {
let result = '' // 存儲新字元串,用於合併新的字元
let count = 0 // 記錄初始狀態,因為第一個字母需要小寫
let space = false // 記錄字元狀態,是否出現已經特殊字元
for (let i = 0; i < str.length; i++) {
if (str[i].search(/^[A-Za-z]+$/) === 0) {
if (count === 0 && space === false) {
count++
} // 此時count恆等於1,根據下麵的三元表達式第一次出現的字母為小寫
result = space === true && count !== 0
? result.concat(str[i].toUpperCase())
: result.concat(str[i].toLowerCase())
space = false // 第一次出現的字母和首字母大寫後的字母都為小寫,設為false
} else {
space = true
// 出現特殊字元space設為true。下一次若是字母則三元表達式的值為true,新字元串合併首字母大寫。
}
}
return result
}
// 短語輸入測試
let a = toConvertCase('!@# fiRst %^&*( nAmE !@#$ ')
let b = toConvertCase('423432 SEcOnd@3423423 NaME')
let c = toConvertCase('@ThIrd-3NAmE')
console.log(a) // 輸出結果:firstName
console.log(b) // 輸出結果:secondName
console.log(c) // 輸出結果:thirdName
只要輸入有特殊字元間隔的短語就能返回小駝峰式命名,容錯率還是非常高的。獲取的字元串特征如下:
- 短語之間會存在特殊字元間隔,利用這個作為實現切入點。
- 第一個單詞的首字母為小寫,後面的單詞首字母都為大寫。
可以根據上面的字元串特征進行解題,不過上面的使用好像有點超綱了。我們還是重點關註它怎麼用就行了。
字元串刪除與截取(重要)
方法 | 說明 |
---|---|
String.prototype.slice() |
提取字元串的一部分並返回一個新字元串。 |
String.prototype.substring() |
返回一個新字元串,其中包含來自(或之間)指定索引(或多個索引)的調用字元串的字元。 |
字元串的slice
方法跟數組的slice
方法操作基本相同。接收兩個參數為開始和結束位置(不包括)進行字元提取。我們重點關註substring
方法,這個是最常用的字元串截取操作。substring
方法接收兩個參數,這裡暫且設為start
和end
兩個參數,分別代表開始和結束位置(不包括)的字元。使用規則如下:
start === end
返回一個空字元串- 省略
end
參數,substring
方法直接提取到末尾 - 任一參數小於
0
或為NaN
,會當作0
進行截取 - 任一參數大於字元串長度,則會截取到
array.length
start > end
會進行參數調換再截取字元串
let a = "abcdefg"
a.substring(2, 2) // 輸出結果:''
a.substring(2) // 輸出結果:'cdefg'
a.substring(2, NaN) // 輸出結果:'ab'
a.substring(8, 2) // 輸出結果:'cdefg'
a.substring(4, 2) // 輸出結果:'cd'
參考