本文整理靈感來源掘金大佬的:數(shù)據(jù)結(jié)構(gòu) - 數(shù)組叹哭。感謝大佬的整理和分享卿泽,此文章主要記錄數(shù)組的主要 API
和自己的一些見解熄攘,僅供自己學(xué)習(xí)參考!J〉辍!說實(shí)話數(shù)組對(duì)于 JavaScript
來說絕對(duì)是萬丈高樓的地基工禾,重要性不言而喻,話不多說開整。
數(shù)組的創(chuàng)建
let a = []
let b = new Array()
一般是以上兩種贼穆,都是用來創(chuàng)建一個(gè)空數(shù)組奴潘。如果我們想初始化一個(gè)有長(zhǎng)度的數(shù)組,并且數(shù)組一開始就有值,如下栗子:
let a = [1, 2, 3]; // [1, 2, 3]
let b = new Array(3); // [undefined, undefined, undefined]
let c = new Array([1, 2, 3]); // [1, 2, 3]
let d = new Array(3).fill(1); // [1, 1, 1]
let e = new Array(3).fill([]); // [Array(0), Array(0), Array(0)]
let f = Array.from({ length : 5}, function() { // [1, 1, 1, 1, 1]
return 1
})
前面三個(gè)估計(jì)大家都看得懂喉誊,后面三個(gè)可能大部分沒見過,都屬于 ES6
新增的創(chuàng)建方式铺呵,這里了解即可裹驰,具體應(yīng)用可以在實(shí)際開發(fā)中碰到在深入了解,其實(shí)基本用的也很少~~~
如何訪問數(shù)組
我們平時(shí)使用的 [1, 2, 3]
這種形式片挂,稱為一維數(shù)組幻林。而如果數(shù)組中嵌套數(shù)組,每嵌套多一層音念,就加一個(gè)維度沪饺。記住數(shù)組的下標(biāo)是從 0 開始的。如果某個(gè)程序猿跟你表白說你是它心中第 0 位的人……你還是拒絕 “他” 吧闷愤,已經(jīng)走火入魔沒救了整葡。
- 一維數(shù)組
let a = [1, 2, 3];
console.log(arr[0]); // 1
console.log(arr[1]); // 2
console.log(arr[2]); // 3
- 二維數(shù)組
let arr = [[1, 2, 3], [4, 5]]
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr[i].length; j++) {
console.log(arr[i][j])
}
}
- 三維數(shù)組
let arr = [[1, 2, [3, 4]], [5, 6]]
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr[i].length; j++) {
for (let k = 0; k < arr[i][j].length; k++) {
console.log(arr[i][j][k])
}
}
}
更高維度的數(shù)組訪問依次類推即可!但是這里延伸一個(gè)問題讥脐,多維數(shù)組的訪問異常麻煩遭居,開發(fā)中我們可能經(jīng)常會(huì)遇到這種需求:將多維數(shù)組扁平化處理為一維數(shù)組?如何實(shí)現(xiàn)呢旬渠,可以自己先研究研究俱萍,這里會(huì)將代碼放在
forEach()
這個(gè)API
中,畢竟咱要先記錄數(shù)組的一些主要方法在去研究如何扁平化告丢,循序漸進(jìn)+延伸思考才是程序猿精神G鼓ⅰ!岖免!
數(shù)組的增刪改查
我們知道:數(shù)組是最簡(jiǎn)單的內(nèi)存數(shù)據(jù)結(jié)構(gòu)岳颇,而增刪改查在任何一種數(shù)據(jù)結(jié)構(gòu)中都是基礎(chǔ)。比如前端:DOM
樹形結(jié)構(gòu)的增刪改查颅湘,比如服務(wù)端:SQL
的增刪改查话侧。
我們了解數(shù)組增刪改查的同時(shí),應(yīng)該代入自己更深入的思考栅炒,使用數(shù)組提供給我們的
API
實(shí)現(xiàn)功能的同時(shí)掂摔,要同時(shí)去了解它的返回值是什么,是否對(duì)原數(shù)組造成影響赢赊。
- 數(shù)組的新增
push()
方法:可向數(shù)組的末尾添加一個(gè)或多個(gè)元素,并返回新的長(zhǎng)度级历,常用語法為:arr.push(newelement1,newelement2,....,newelementX)
释移,使用該方法會(huì)改變?cè)瓟?shù)組。
let arr = [1, 2, 3, 4]
console.log(arr.push(5)) // 5
console.log(arr) // [1, 2, 3, 4, 5]
console.log(arr.push(6,7,8)) // 8
console.log(arr) // [1, 2, 3, 4, 5, 6, 7, 8]
unshift()
方法:可向數(shù)組的開頭添加一個(gè)或更多元素寥殖,并返回新的長(zhǎng)度玩讳,常用語法為:arr.unshift(newelement1,newelement2,....,newelementX)
涩蜘,使用該方法會(huì)改變?cè)瓟?shù)組。
let arr = [1, 2, 3, 4]
console.log(arr.unshift(5)) // 5
console.log(arr) // [5, 1, 2, 3, 4]
console.log(arr.unshift(6,7,8)) // 8
console.log(arr) // [6, 7, 8, 5, 1, 2, 3, 4]
當(dāng)然掘金博主的文章里還介紹了幾種熏纯,如:arr[arr.length] = 5
或者 arr[0] = 1
這種同诫;亦或者 splice()
和 concat()
等操作也可以實(shí)現(xiàn)新增。前者其實(shí)也是數(shù)組的一些小技巧應(yīng)用樟澜,后者其實(shí)這些 API
有自己更深層次的應(yīng)用误窖,日常開發(fā)中用的最多的新增操作還是 push()
。學(xué)而思學(xué)而用才是我們循序進(jìn)步的基礎(chǔ)V确 E场!
- 數(shù)組的刪除
pop()
方法:刪除數(shù)組的最后一個(gè)元素并返回被刪除的元素毒费,使用該方法會(huì)改變?cè)瓟?shù)組丙唧。
let arr = [1, 2, 3, 4, 5, 6]
console.log(arr.pop()) // 6
console.log(arr) // [1, 2, 3, 4, 5]
// 嘗試傳參
let arr = [1, 2, 3, 4, 5]
arr.pop(3) // 傳參無效,只會(huì)刪除數(shù)組的最后一個(gè)元素
console.log(arr) // [1, 2, 3, 4]
shift()
方法:把數(shù)組的第一個(gè)元素從其中刪除觅玻,并返回第一個(gè)元素的值想际,使用該方法會(huì)改變?cè)瓟?shù)組。
let arr = [1, 2, 3, 4, 5]
console.log(arr.shift()) // 1
console.log(arr) // [2, 3, 4, 5]
// 傳參情況和 pop() 類似溪厘,不接受傳參
上面兩個(gè)刪除的方式只能適用比較傻瓜的場(chǎng)景胡本,所以數(shù)組也給我們提供了萬金油的刪改 API
,接下來認(rèn)識(shí)老油條:slice() 和 splice()
桩匪,日常開發(fā)中非常常用的兩個(gè) API
打瘪,因?yàn)樗鼈z實(shí)在太優(yōu)秀了,所以我必須在這里寫一段話~~~
slice()
方法:可從已有的數(shù)組中返回選定的元素傻昙。這里要注意:使用 slice()
會(huì)返回一個(gè)新的數(shù)組而不是修改原數(shù)組闺骚。
不得不說,這個(gè)方法牛逼哄哄妆档!數(shù)組的拷貝僻爽、刪除都可以通過這個(gè)方法來實(shí)現(xiàn),具體功能取決于 ()
里面?zhèn)鬟f參數(shù)的個(gè)數(shù)贾惦。
第一種情況:不傳參胸梆,此時(shí)會(huì)對(duì)原數(shù)組進(jìn)行深拷貝并生成一個(gè)新數(shù)組,即如果我們修改新數(shù)組的值不會(huì)影響原數(shù)組须板。深拷貝淺拷貝搞不明白的可以瞅瞅我寫的:JavaScript基礎(chǔ)篇(一)
let arr = [1, 2, 3, 4, 5]
let newArr = arr.slice()
newArr[0] = 6
console.log(arr) // [1, 2, 3, 4, 5]
console.log(newArr) // [6, 2, 3, 4, 5]
第二種情況:傳入一個(gè)參數(shù)且為正值碰镜,此時(shí)將原數(shù)組從下標(biāo)為傳遞的參數(shù)值開始一直截取到最后。
let arr = [1, 2, 3, 4, 5]
let newArr = arr.slice(1)
console.log(newArr) // [2, 3, 4, 5]
第三種情況:傳入一個(gè)參數(shù)且為負(fù)值习瑰,此時(shí)將截取原數(shù)組末尾的元素绪颖,傳遞的參數(shù)為多少,就在后面截取多少個(gè)甜奄。
let arr = [1, 2, 3, 4, 5]
let newArr = arr.slice(-2)
console.log(newArr) // [4, 5]
第四種情況:傳入兩個(gè)參數(shù)柠横,第一個(gè)參數(shù)為數(shù)組切割的起始值窃款,第二個(gè)參數(shù)為數(shù)組切割的結(jié)束值。記住切割的小秘訣:包頭不包尾牍氛,例如(1晨继,4)就是下標(biāo)為 1 的元素就要算進(jìn)去,但是下標(biāo)為 4 的元素不會(huì)算進(jìn)去搬俊。
let arr = [1, 2, 3, 4, 5]
let newArr = arr.slice(1, 4)
console.log(newArr) // [2, 3, 4]
slice()
基本的使用場(chǎng)景就是上面這些紊扬,最后總結(jié)一下它的語法:arr.slice(beginSlice, endSlice)
參數(shù) | 是否必填 | 參數(shù)描述 |
---|---|---|
beginSlice | 否 | 從該索引(以 0 為基數(shù))處開始提取原字符串中的字符。 |
endSlice | 否 | 結(jié)束位置(以 0 為基數(shù))悠抹,如果不傳珠月,默認(rèn)到數(shù)組末尾。 |
我看 w3school 中好像規(guī)定 beginSlice
為必傳參數(shù)楔敌,但是寫法中確實(shí)可以不傳啤挎,如不傳會(huì)進(jìn)行深拷貝生成一個(gè)新的數(shù)組,個(gè)人感覺應(yīng)該是該方法的基礎(chǔ)函數(shù)中做了傳參的兼容處理卵凑,其實(shí)不一定要傳庆聘。(本理解僅供自己參考)
splice()
方法:向/從數(shù)組中添加/刪除項(xiàng)目,然后返回被刪除的項(xiàng)目勺卢。
相較于 slice()
多了一個(gè) p
伙判,它和 slice()
一樣也是個(gè)萬金油的方法,它能適用于新增黑忱、修改宴抚、刪除這些場(chǎng)景。但是這里一定要記住它和 slice()
區(qū)別最大的一點(diǎn):
splice():所進(jìn)行的操作會(huì)影響到原數(shù)組
slice():所進(jìn)行的操作不會(huì)影響到原數(shù)組
第一種情況:不傳參甫煞,自身返回一個(gè)空數(shù)組菇曲,原數(shù)組不改變。官方同樣也規(guī)定了第一個(gè)參數(shù)和第二個(gè)參數(shù)為必傳項(xiàng)抚吠,此處主要是和 slice()
做對(duì)比演示常潮。(不推薦)
let arr = [1, 2, 3, 4, 5]
console.log(arr.splice()) // []
console.log(arr)
第二種情況:傳一個(gè)參數(shù),當(dāng) splice()
傳入一個(gè)參數(shù)時(shí)楷力,它的基礎(chǔ)函數(shù)應(yīng)該做了判斷喊式,第二個(gè)參數(shù)不寫應(yīng)該是第一個(gè)參數(shù)后面的元素全部刪除。并且同 slice()
一樣萧朝,它也支持傳遞負(fù)數(shù)岔留,即從項(xiàng)目的末尾位置開始刪除,執(zhí)行 splice()
返回被刪除的元素集合检柬。該方法會(huì)改變?cè)瓟?shù)組贸诚。如下栗子:
let arr = [1, 2, 3, 4, 5]
console.log(arr.splice(3)) // [4, 5]
console.log(arr) // [1, 2, 3]
// 如果傳遞負(fù)數(shù)要刷出 4 和 5
console.log(arr.splice(-2)) // [4, 5]
console.log(arr) // [1, 2, 3]
第三種情況:傳兩個(gè)參數(shù),第一個(gè)參數(shù)代表要?jiǎng)h除項(xiàng)目的位置(使用負(fù)數(shù)可從數(shù)組結(jié)尾處規(guī)定位置厕吉。)酱固,第二個(gè)參數(shù)代表要?jiǎng)h除的項(xiàng)目數(shù)量。官方有規(guī)定使用該方法必須傳入兩個(gè)參數(shù)头朱,當(dāng)然只傳遞一個(gè)參數(shù)也不會(huì)報(bào)錯(cuò)运悲,不過可能嚴(yán)謹(jǐn)性不夠。
let arr = [1, 2, 3, 4, 5]
console.log(arr.splice(1, 2)) // [2, 3]
console.log(arr) // [1, 4, 5]
// 第一個(gè)參數(shù)為負(fù)值的情況
console.log(arr.splice(-1, 1)) // [5]
console.log(arr) // [1, 2, 3, 4]
第四種情況:傳入三個(gè)參數(shù)项钮,前面基本都是刪除班眯,但是如果我們傳入三個(gè)參數(shù),該方法又解鎖了新增和修改兩個(gè)功能烁巫,有木有覺得很巧妙署隘,來看看栗子:
// 新增操作
let arr = [1, 2, 3, 4, 5]
console.log(arr.splice(1, 0, 6)) // []
console.log(arr) // [1, 6, 2, 3, 4, 5]
// 同時(shí)新增多個(gè)元素
let arr = [1, 2, 3, 4, 5]
console.log(arr.splice(1, 0, 6, 7, 8, 9)) // []
console.log(arr) // [1, 6, 7, 8, 9, 2, 3, 4, 5]
// 修改操作
let arr = [1, 2, 3, 4, 5]
console.log(arr.splice(1, 1, 6)) // [2]
console.log(arr) // [1, 6, 3, 4, 5]
// 修改+新增雙合一
let arr = [1, 2, 3, 4, 5]
console.log(arr.splice(1, 1, 6, 7, 8, 9)) // [2]
console.log(arr) // [1, 6, 7, 8, 9, 3, 4, 5]
看了上面的栗子有木有覺得恍然大悟(有點(diǎn)懵逼),簡(jiǎn)單總結(jié):
- 第二個(gè)參數(shù)為
0
即代表新增亚隙,將第三個(gè)(及其后面的參數(shù)) 追加到數(shù)組的下標(biāo)(第一個(gè)參數(shù))元素后面去磁餐,自身返回一個(gè)空數(shù)組,同時(shí)該操作會(huì)改變?cè)亟M阿弃。 - 第二個(gè)參數(shù)為
1
即代表修改诊霹,要修改的元素為數(shù)組的下標(biāo)(第一個(gè)參數(shù))元素,將其改為第三個(gè)參數(shù)(及其后面的參數(shù))渣淳,自身返回被修改的元素集合(數(shù)組)脾还,同時(shí)該操作會(huì)改變?cè)亟M。
用法其實(shí)也不復(fù)雜入愧,可能有點(diǎn)繞鄙漏,我們也做個(gè)表來對(duì)該方法做個(gè)總結(jié):
參數(shù) | 是否必填 | 參數(shù)描述 |
---|---|---|
index | 是 | 必需。整數(shù)棺蛛,規(guī)定添加/刪除項(xiàng)目的位置怔蚌,使用負(fù)數(shù)可從數(shù)組結(jié)尾處規(guī)定位置。 |
howmany | 是 | 必需鞠值。要?jiǎng)h除的項(xiàng)目數(shù)量媚创。如果設(shè)置為 0,則不會(huì)刪除項(xiàng)目彤恶。 |
item1, ..., itemX | 否 | 可選钞钙。向數(shù)組添加的新項(xiàng)目。 |
對(duì)比
pop()
声离、shift()
芒炼、slice()
、splice()
應(yīng)用場(chǎng)景和主要區(qū)別术徊?
pop() 和 shift()
:使用場(chǎng)景類似本刽,一個(gè)尾部刪除,一個(gè)頭部刪除,兩者刪除都返回被刪除的元素子寓,返回類型為被刪除的元素自身的類型暗挑。
slice()
:可以用來做深拷貝、也可以用來刪除數(shù)組中的元素斜友,該方法最大的特點(diǎn)就是它自身返回生成一個(gè)新的數(shù)組且所有操作都不影響原數(shù)組炸裆。
splice()
:一個(gè)更全面的API
,集合了修改鲜屏、刪除烹看、新增功能,自身返回類型同樣是一個(gè)數(shù)組洛史。唯一的副作用可能就是所有操作都會(huì)影響原數(shù)組惯殊。
- 數(shù)組的修改
最簡(jiǎn)單場(chǎng)景的修改,不借助任何 API
的方式:
let arr = [1, 2, 3, 4, 5]
// 通過索引下標(biāo)修改元素
arr[0] = 6
console.log(arr) // [6, 2, 3, 4, 5]
使用 splice()
進(jìn)行修改也殖,刪除里面詳細(xì)講解了這個(gè)方法土思,這里就不贅述了!我看掘金的博主大大在修改這個(gè)模塊下面整理了 filter()
方法毕源,該方法主要的功能其實(shí)是對(duì)數(shù)組進(jìn)行過濾處理浪漠,并且該方法不會(huì)改變?cè)瓟?shù)組,而是生成一個(gè)新的數(shù)組霎褐,所以感覺定義在修改這里是不是有點(diǎn)勉強(qiáng)~~~
- 數(shù)組的查詢
查詢我們肯定要知道查詢條件是什么址愿,所以其實(shí)數(shù)組查詢一般都是通過遍歷數(shù)組,匹配對(duì)應(yīng)的查詢條件來實(shí)現(xiàn)的冻璃。其實(shí)這個(gè)方法也比較多响谓,例如剛剛說的 filter()
數(shù)組過濾處理,也算是一種簡(jiǎn)單查詢省艳;通過 map()
進(jìn)行遍歷匹配也可以算查詢娘纷;還有 find()
和 findIndex()
,感覺這些 API
名字取得也都比較應(yīng)景跋炕;還有 indexOf()
和 lastIndexOf()
等等赖晶,這里我們也簡(jiǎn)單拿幾個(gè)栗子來演示:
indexOf()
方法:可返回?cái)?shù)組中某個(gè)指定的元素位置,常用語法為:array.indexOf(item,start)
辐烂。
-
item
:必須遏插。查找的元素。 -
start
:可選的整數(shù)參數(shù)纠修。規(guī)定在數(shù)組中開始檢索的位置胳嘲。它的合法取值是 0 到 stringObject.length - 1。如省略該參數(shù)扣草,則將從字符串的首字符開始檢索了牛。 - 返回值:
Number
類型颜屠,元素在數(shù)組中的位置,如果沒有搜索到則返回 -1鹰祸。
const arr = [1, 2, 3, 4, 5, 1, 3, 4, 2, 5]
// 數(shù)組中有很多個(gè) 1甫窟,返回的是第一個(gè) 1 出現(xiàn)的位置索引
console.log(arr.indexOf(1)) // 0
// 第二個(gè)參數(shù)表示從數(shù)組中的第五個(gè)元素開始查找,所以索引為 0 的 1 被自動(dòng)忽略
console.log(arr.indexOf(1, 4)) // 5
lastIndexOf()
方法:返回一個(gè)指定的元素在數(shù)組中最后出現(xiàn)的位置福荸,從該字符串的后面向前查找蕴坪。常用語法為:array.lastIndexOf(item,start)
。
-
item
:必須敬锐。查找的元素。 -
start
:可選的整數(shù)參數(shù)呆瞻。規(guī)定在字符串中開始檢索的位置台夺。它的合法取值是 0 到 stringObject.length - 1。如省略該參數(shù)痴脾,則將從字符串的最后一個(gè)字符處開始檢索颤介。 - 返回值:
Number
類型,元素在數(shù)組中的位置赞赖,返回元素在數(shù)組中最后一次出現(xiàn)的索引位置滚朵,如果沒有搜索到則返回 -1
const arr = [1, 2, 3, 4, 5, 1, 3, 4, 2, 5]
// 返回?cái)?shù)組中最后一個(gè) 5 所在的位置索引
console.log(arr.lastIndexOf(5)) // 9
console.log(arr.lastIndexOf(9)) // -1
console.log(arr.lastIndexOf(4, 2)) // -1
console.log(arr.lastIndexOf(4, 4)) // 3
有沒有感覺 lastIndexOf(4, 4)
的第二個(gè)參數(shù)很奇怪,其實(shí)它的第二個(gè)參數(shù)代表數(shù)組中的元素截取點(diǎn)前域,如 arr.lastIndexOf(4, 2)
就是數(shù)組中的前三位里面 [1, 2, 3]
里面有沒有 4 這個(gè)元素所以返回 -1
辕近,而 arr.lastIndexOf(4, 4)
就是數(shù)組中的前五位里面 [1, 2, 3, 4, 5]
中包含了 4 且它在數(shù)組中的的索引位置為 3 所以返回 3。
includes()
方法:判斷一個(gè)數(shù)組是否包含一個(gè)指定的值匿垄,如果是返回 true移宅,否則false。常用語法:arr.includes(searchElement, fromIndex)
椿疗。
-
searchElement
:必須漏峰。需要查找的元素值。 -
fromIndex
:可選届榄。從該索引處開始查找 searchElement浅乔。如果為負(fù)值,則按升序從 array.length + fromIndex 的索引開始搜索铝条。默認(rèn)為 0靖苇。 - 返回值:
Boolean
類型,如果找到指定值返回 true攻晒,否則返回 false顾复。
const arr = [1, 2, 3, 4, 5, 1, 3, 7, 4, 2, 5]
console.log(arr.includes(2)) // true
// 數(shù)組中前2個(gè)元素是否包含2,1為數(shù)組截取的下標(biāo)索引鲁捏,即會(huì)截取[1, 2]在進(jìn)行判定
console.log(arr.includes(2, 1)) // true
// 第二個(gè)參數(shù)為負(fù)數(shù)時(shí)-2代表截取的數(shù)組長(zhǎng)度而不是索引芯砸,即會(huì)截取[2, 5]來進(jìn)行判定
console.log(arr.includes(2, -2)) // true
當(dāng)然萧芙,如果我們打算用 indexOf()
或者 lastIndexOf()
或者 includes()
來對(duì)一些復(fù)雜的數(shù)組結(jié)構(gòu)進(jìn)行查找肯定是不行的,就比如下面這個(gè)栗子:
const list = [
{ id: 1, name: 'zhangsan' },
{ id: 2, name: 'lisi' },
{ id: 3, name: 'wangwu' }
]
如果我們此時(shí)去判斷 list
數(shù)組中是否有 zhangsan
假丧,我們首先嘗試用 indexOf()
的方式來驗(yàn)證一下:
list.indexOf('zhangsan') // -1
果然不出所料双揪,這個(gè)辦法是不行的!此時(shí)我們就需要對(duì)數(shù)組進(jìn)行遍歷包帚,然后對(duì)每個(gè)對(duì)象進(jìn)行匹配查找渔期,來看看 find()
這個(gè)語義話的方法吧:
const list = [
{ id: 1, name: 'zhangsan' },
{ id: 2, name: 'lisi' },
{ id: 3, name: 'wangwu' }
]
const res = list.find(item => {
return item.name === 'zhangsan'
})
console.log(res) // {id: 1, name: "zhangsan"}
我們這里先不糾結(jié) find()
的規(guī)則,因?yàn)樗举|(zhì)上是遍歷數(shù)組中的每一項(xiàng)然后去根據(jù)條件匹配渴邦,而數(shù)組中的遍歷方式有非常多 API
疯趟,稍后我們會(huì)對(duì)這些 API
逐個(gè)分析比較其優(yōu)劣勢(shì),這里先只演示查找的用法谋梭。上面的栗子中我們根據(jù) name==='zhangsan'
查找對(duì)了它所在的元素對(duì)象信峻,這里安利一個(gè)箭頭函數(shù)返回值的寫法,就是箭頭函數(shù)中如果后面直接跟判斷條件返回的話不需要寫 return
瓮床,可以直接使用下面這種寫法:
// 一行搞定盹舞,直接跟判斷條件這樣寫默認(rèn)直接返回
const res = list.find(item => item.name === 'zhangsan')
console.log(res) // {id: 1, name: "zhangsan"}
再來看看 findIndex()
這個(gè)方法,多么語義化的 API
啊隘庄,看名字就知道是找對(duì)應(yīng)條件元素的索引踢步,直接上栗子:
const list = [
{ id: 1, name: 'zhangsan' },
{ id: 2, name: 'lisi' },
{ id: 3, name: 'wangwu' }
]
const res = list.findIndex(item => item.name == 'wangwu')
// wangwu 在數(shù)組中的第三個(gè)元素對(duì)象里面,其索引下標(biāo)為 2
console.log(res) // 2
當(dāng)然丑掺,正如我們前面所說的获印,其實(shí)數(shù)組的查詢有很多個(gè) API
可以用,畢竟查詢無非就是遍歷數(shù)組進(jìn)行條件匹配吼鱼,而數(shù)組遍歷的 API
真的太多了蓬豁,基礎(chǔ)的查找方法就介紹上面 5 種,接下來讓我們一起認(rèn)識(shí)數(shù)組遍歷吧9剿唷5胤唷!
數(shù)組的遍歷
遍歷不會(huì)琐谤,學(xué)了也廢~~~感覺 JavaScript
中用的最多的就是遍歷蟆技,不管是數(shù)組遍歷還是對(duì)象遍歷,基本開發(fā)中各種數(shù)據(jù)的處理都離不開他們斗忌,這里我們就羅列一些常用的數(shù)組遍歷的 API
质礼,寫一寫,記一記织阳,代碼真容易?艚丁!唧躲!
find() 方法
返回?cái)?shù)組中滿足提供的測(cè)試函數(shù)的第一個(gè)元素的值造挽,沒有則返回 undefined
碱璃。常用語法為:arr.find(callback[, thisArg])
。該方法為 ES6
新增方法饭入。
-
callback
:在數(shù)組每一項(xiàng)上執(zhí)行的函數(shù)嵌器,接收三個(gè)參數(shù):
element
:必填,當(dāng)前遍歷到的元素
index
:可選谐丢,當(dāng)前遍歷到的索引
array
:可選爽航,數(shù)組本身 -
thisArg
:可選,執(zhí)行回調(diào)時(shí)用作this
的對(duì)象 - 返回值:數(shù)組中第一個(gè)滿足所提供測(cè)試函數(shù)的元素的值乾忱,沒有則返回
undefined
讥珍,find()
方法不會(huì)改變?cè)瓟?shù)組。
// 返回值測(cè)試
const array = [5, 12, 8, 130, 44]
console.log(array.find(item => item > 10)) // 12 返回?cái)?shù)組中第一個(gè)符合條件的值
// 常用語法測(cè)試
const list = [
{ id: 1, name: 'zhangsan' },
{ id: 2, name: 'lisi' },
{ id: 3, name: 'wangwu' }
]
const list1 = [
{ id: 10, name: 'zhaoliu' }
]
function findName(list) {
return list.find(function (item, index, elem) {
// item 就是 list 數(shù)組中的每一項(xiàng)
// index 就是每一項(xiàng)的索引
// elem 就是 list 本身
console.log(item, index, elem)
console.log(this) // [{ id: 10, name: 'zhaoliu' }]
console.log(this[0].id) // 10
}, list1) // list1 就是函數(shù)中的 this 指向饭耳,無則使用 undefined
}
findName(list)
活學(xué)活用小測(cè)試:尋找數(shù)組中的質(zhì)數(shù)串述,如果找不到質(zhì)數(shù)則返回 undefined
。質(zhì)數(shù)是啥......只能被1和自身整除的數(shù)就是質(zhì)數(shù)寞肖,比如 3,只有 1 和 3 能整除衰腌,而 4 有 1新蟆、2、4 都能整除右蕊,那么 3 就是質(zhì)數(shù)琼稻,4就是合數(shù)。好吧饶囚,原理摸清楚了帕翻,該寫代碼了:
const arr = [4, 6, 9]
const arr1 = [4, 11, 6, 8, 9, 10]
function isPrime(item, index, array) {
let start = 2
while (Math.sqrt(item) >= start) {
if (item % start++ < 1) {
return false
}
}
return item > 1
}
console.log(arr.find(isPrime)) // undefined
console.log(arr1.find(isPrime)) // 11
findIndex() 方法
返回?cái)?shù)組中滿足提供的測(cè)試函數(shù)的第一個(gè)元素的索引。若沒有找到對(duì)應(yīng)元素則返回-1萝风。常用語法為:arr.findIndex(callback[, thisArg])
嘀掸。該方法為 ES6
新增方法。
-
callback
:針對(duì)數(shù)組中的每個(gè)元素, 都會(huì)執(zhí)行該回調(diào)函數(shù), 執(zhí)行時(shí)會(huì)自動(dòng)傳入下面三個(gè)參數(shù):
element
:當(dāng)前函數(shù)
index
:當(dāng)前元素的索引
array
:調(diào)用findIndex
的數(shù)組 -
thisArg
:可選规惰,執(zhí)行回調(diào)時(shí)作為this
對(duì)象的值睬塌。 - 返回值:數(shù)組中通過提供測(cè)試函數(shù)的第一個(gè)元素的索引。否則歇万,返回 -1揩晴,
findIndex()
不會(huì)修改所調(diào)用的數(shù)組。
// 返回值測(cè)試
const array = [5, 12, 8, 130, 44]
console.log(array.findIndex(item => item > 10)) // 1 滿足條件的第一個(gè)元素為 12贪磺,其索引為 1硫兰。
// 常用語法測(cè)試,基本和 find() 差不多寒锚,直接借用過來了
const list = [
{ id: 1, name: 'zhangsan' },
{ id: 2, name: 'lisi' },
{ id: 3, name: 'wangwu' }
]
const list1 = [
{ id: 10, name: 'zhaoliu' }
]
function findName(list) {
return list.findIndex(function (item, index, elem) {
// item 就是 list 數(shù)組中的每一項(xiàng)
// index 就是每一項(xiàng)的索引
// elem 就是 list 本身
console.log(item, index, elem)
console.log(this) // [{ id: 10, name: 'zhaoliu' }]
console.log(this[0].id) // 10
}, list1) // list1 就是函數(shù)中的 this 指向劫映,無則使用 undefined
}
findName(list)
小測(cè)試:查找數(shù)組中收個(gè)質(zhì)數(shù)元素的索引违孝,如找不到質(zhì)數(shù),則返回-1苏研。
const arr = [4, 6, 9]
const arr1 = [4, 11, 6, 8, 9, 10]
function isPrime(item, index, array) {
let start = 2
while (Math.sqrt(item) >= start) {
if (item % start++ < 1) {
return false
}
}
return item > 1
}
console.log(arr.findIndex(isPrime)) // -1
console.log(arr1.findIndex(isPrime)) // 1
map() 方法
返回一個(gè)新數(shù)組等浊,數(shù)組中的元素為原始數(shù)組元素調(diào)用函數(shù)處理后的值。該方法按照原始數(shù)組元素順序依次處理元素摹蘑。map() 不會(huì)對(duì)空數(shù)組進(jìn)行檢測(cè)筹燕, map()
不會(huì)改變?cè)紨?shù)組。常用語法為:arr.map(function(currentValue,index,arr), thisValue)
衅鹿。
-
function(currentValue, index,arr)
:必須撒踪。函數(shù),數(shù)組中的每個(gè)元素都會(huì)執(zhí)行這個(gè)函數(shù)
currentValue
:必須大渤。當(dāng)前元素的值
index
:可選制妄。當(dāng)前元素的索引值
arr
:調(diào)用map
方法的數(shù)組 -
thisValue
:可選。對(duì)象作為該執(zhí)行回調(diào)時(shí)使用泵三,傳遞給函數(shù)耕捞,用作 "this" 的值。
如果省略了 thisValue烫幕,或者傳入 null俺抽、undefined,那么回調(diào)函數(shù)的 this 為全局對(duì)象较曼。 - 返回值:一個(gè)由原數(shù)組每個(gè)元素執(zhí)行回調(diào)函數(shù)的結(jié)果組成的新數(shù)組磷斧。
// 返回值測(cè)試
const numbers = [1, 4, 9]
const roots = numbers.map(Math.sqrt)
console.log(roots) // [1, 2, 3]
console.log(numbers) // [1, 4, 9]
// 使用 map 重新格式化數(shù)組中的對(duì)象
const kvArray = [{ key: 1, value: 10 },
{ key: 2, value: 20 },
{ key: 3, value: 30 }];
// 其實(shí)只有 obj 有用,只是將所有可選參數(shù)羅列出來
const reformattedArray = kvArray.map((obj, index, array) => {
let rObj = {}
rObj[obj.key] = obj.value
return rObj
})
console.log(reformattedArray) // [{1: 10}, {2: 20}, {3: 30}]
通常情況下捷犹,map 方法中的 callback 函數(shù)只需要接受一個(gè)參數(shù)弛饭,就是正在被遍歷的數(shù)組元素本身。但這并不意味著 map 只給 callback 傳了一個(gè)參數(shù)萍歉。這個(gè)思維慣性可能會(huì)讓我們犯一個(gè)很容易犯的錯(cuò)誤侣颂。參考下例:
['1', '2', '3'].map(parseInt)
返回結(jié)果是什么?
const res = ['1', '2', '3'].map(parseInt)
console.log(res) // [1, NaN, NaN]
我們期望輸出 [1, 2, 3], 而實(shí)際結(jié)果是 [1, NaN, NaN]翠桦。我估計(jì)第一眼看都會(huì)有點(diǎn)懵横蜒,為啥會(huì)這樣返回,我們可以將其進(jìn)行拆解:
// 因?yàn)?map 方法本身會(huì)返回一個(gè)新的數(shù)組销凑,拆解出來后其實(shí)就是分別返回
// parseInt('1', 0) 1
// parseInt('2', 1) NaN
// parseInt('3', 2) NaN
['1', '2', '3'].map((num, index) => {
return parseInt(num, index)
})
所以其實(shí)這里的問題變成了 parseInt(num, index)
的值返回的是一個(gè)什么結(jié)果丛晌,我們可以去查一下 parseInt
的用法:
parseInt() 函數(shù)可解析一個(gè)字符串,并返回一個(gè)整數(shù)斗幼,常用語法:
parseInt(string, radix)
參數(shù) | 描述 |
---|---|
string | 必需澎蛛。要被解析的字符串。 |
radix | 可選蜕窿。表示要解析的數(shù)字的基數(shù)谋逻。該值介于 2 ~ 36 之間呆馁。<br />如果省略該參數(shù)或其值為 0,則數(shù)字將以 10 為基礎(chǔ)來解析毁兆。如果它以 “0x” 或 “0X” 開頭浙滤,將以 16 為基數(shù)。<br />如果該參數(shù)小于 2 或者大于 36气堕,則 parseInt() 將返回 NaN纺腊。 |
好吧,擴(kuò)展了解一下>グ拧R灸ぁ!MDN 上關(guān)于這個(gè)問題有更深入的剖析梅桩,感興趣的可以去瞅瞅壹粟,此理解僅供參考~~~
forEach() 方法
用于調(diào)用數(shù)組的每個(gè)元素,并將元素傳遞給回調(diào)函數(shù)宿百。該方法對(duì)于空數(shù)組是不會(huì)執(zhí)行回調(diào)函數(shù)的趁仙。常用語法為:arr.forEach(function(currentValue, index, arr), thisValue)
。使用該方法不會(huì)改變?cè)瓟?shù)組垦页。
-
function(currentValue, index,arr)
:必須幸撕。函數(shù),數(shù)組中每個(gè)元素需要調(diào)用的函數(shù)
currentValue
:必須外臂。當(dāng)前元素
index
:可選。當(dāng)前元素的索引值
arr
:當(dāng)前元素所屬的數(shù)組對(duì)象律胀。 -
thisValue
:可選宋光。當(dāng)執(zhí)行回調(diào)函數(shù) callback 時(shí),用作 this 的值炭菌。 - 返回值:
undefined
罪佳。
// 返回值測(cè)試
const arr = ['a', 'b', 'c']
const res = arr.forEach(item => console.log(item)) // a b c
console.log(res) // undefined
除了拋出異常以外,沒有辦法中止或跳出 forEach() 循環(huán)黑低。如果你需要中止或跳出循環(huán)赘艳,forEach() 方法不是應(yīng)當(dāng)使用的工具。還有如果數(shù)組中出現(xiàn)空值或者 undefined
時(shí)執(zhí)行會(huì)被跳過克握,如下栗子:
function logArrayElements(element, index, array) {
console.log('a[' + index + '] = ' + element);
}
// 注意索引 2 被跳過了蕾管,因?yàn)樵跀?shù)組的這個(gè)位置沒有項(xiàng)
[2, 5, , 9].forEach(logArrayElements);
// logs:
// a[0] = 2
// a[1] = 5
// a[3] = 9
延伸思考,通過
forEach()
實(shí)現(xiàn)多維數(shù)組扁平化菩暗?
function flatten(arr) {
let result = []
arr.forEach(item => {
if (Array.isArray(item)) { // 判斷遍歷的子元素是否仍是數(shù)組
// 使用遞歸對(duì)子元素進(jìn)行重復(fù)執(zhí)行
result.push(...flatten(item))
} else {
result.push(item)
}
})
return result
}
const arrs = [1, 2, 3, [4, 5, [6, 7], 8, 9]]
const arr = flatten(arrs)
console.log(arr) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
代碼比較簡(jiǎn)單,不過最新的 ES2019
語法推出了一個(gè)數(shù)組拍平的 API
,可以直接拍平數(shù)組黄虱,如下栗子:
let arr = [1, 2, 3, [4, 5, 6, [7, 8, 9]], 10].flat()
console.log(arr) //[1, 2, 3, 4, 5, 6, Array(3), 10]
沒有徹底怕平,是 bug
嗎掏熬,其實(shí)不是,flat(num)
秒梅,num
可選值默認(rèn)是拍平 1 層旗芬,如果是三維數(shù)組可傳入 2 即拍平兩層,依此類推~~~
let arr = [1, 2, 3, [4, 5, 6, [7, 8, 9]], 10].flat(2)
console.log(arr) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
filter() 方法
創(chuàng)建一個(gè)新的數(shù)組捆蜀,新數(shù)組中的元素是通過檢查指定數(shù)組中符合條件的所有元素疮丛。 filter()
不會(huì)對(duì)空數(shù)組進(jìn)行檢測(cè)。filter()
不會(huì)改變?cè)紨?shù)組漱办。該方法為 ES6
新增方法这刷。常用語法為:array.filter(function(currentValue,index,arr), thisValue)
。
-
function(currentValue, index,arr)
:必須娩井。函數(shù)暇屋,數(shù)組中的每個(gè)元素都會(huì)執(zhí)行這個(gè)函數(shù)
currentValue
:必須。數(shù)組中當(dāng)前正在處理的元素洞辣。
index
:可選咐刨。當(dāng)前元素的索引值。
arr
:當(dāng)前元素所屬的數(shù)組對(duì)象扬霜。 -
thisValue
:可選定鸟。當(dāng)執(zhí)行回調(diào)函數(shù) callback 時(shí),用作 this 的值著瓶。 - 返回值:一個(gè)新的联予、由通過測(cè)試的元素組成的數(shù)組,如果沒有任何數(shù)組元素通過測(cè)試材原,則返回空數(shù)組沸久。。
// 返回值測(cè)試
function isBigEnough(item) {
return item>= 10
}
const arr = [12, 5, 8, 130, 44]
const filterArr = arr.filter(isBigEnough)
console.log(arr) // [12, 5, 8, 130, 44]
console.log(filterArr) // [12, 130, 44]
我們也可以使用 filter()
根據(jù)搜索條件來過濾數(shù)組中的內(nèi)容余蟹,如下栗子:
const fruits = ['apple', 'banana', 'grapes', 'mango', 'orange']
function filterItems(query) {
return fruits.filter(item => {
// 將所有子項(xiàng)轉(zhuǎn)化成小寫在進(jìn)行比對(duì)
return item.toLowerCase().indexOf(query.toLowerCase()) > -1
})
}
const filterArr = filterItems('ap')
console.log(filterArr)
小練習(xí):通過
filter()
實(shí)現(xiàn)數(shù)組去重卷胯?
function unique(arr) {
const res = []
arr.filter(item => {
if (res.indexOf(item) < 0) {
res.push(item)
}
})
return res
}
const arr = [10, 20, 30, 20, 50, 40, 30, 60, 20]
const filterArr = unique(arr)
console.log(filterArr) // [10, 20, 30, 50, 40, 60]
知識(shí)進(jìn)階:使用 Set 實(shí)現(xiàn)數(shù)組去重~~~
function unique(arr) {
const set = new Set(arr)
return [...set]
}
const arr = [10, 20, 30, 20, 50, 40, 30]
const res = unique(arr)
console.log(res) // [10, 20, 30, 50, 40]
every() 方法
測(cè)試一個(gè)數(shù)組內(nèi)的所有元素是否都能通過某個(gè)指定函數(shù)的測(cè)試,它返回一個(gè)布爾值威酒。如果數(shù)組中檢測(cè)到有一個(gè)元素不滿足窑睁,則整個(gè)表達(dá)式返回 false ,且剩余的元素不會(huì)再進(jìn)行檢測(cè)葵孤。如果所有元素都滿足條件担钮,則返回 true。 every()
不會(huì)對(duì)空數(shù)組進(jìn)行檢測(cè)佛呻。every()
不會(huì)改變?cè)紨?shù)組裳朋。該方法為 ES6
新增方法。常用語法為:array.every(function(currentValue,index,arr), thisValue)
。
-
function(currentValue, index,arr)
:必須鲤嫡。函數(shù)送挑,數(shù)組中的每個(gè)元素執(zhí)行一次callback
函數(shù),直到它找到一個(gè)會(huì)使callback
返回 falsy 的元素暖眼。如果發(fā)現(xiàn)了一個(gè)這樣的元素惕耕,every
方法將會(huì)立即返回false
并結(jié)束執(zhí)行。
currentValue
:必須诫肠。當(dāng)前元素的值司澎。
index
:可選。當(dāng)前元素的索引值栋豫。
arr
:當(dāng)前元素所屬的數(shù)組對(duì)象挤安。 -
thisValue
:可選。當(dāng)執(zhí)行回調(diào)函數(shù) callback 時(shí)丧鸯,用作 this 的值蛤铜。 - 返回值:布爾值。如果所有元素都通過檢測(cè)返回 true丛肢,否則返回 false围肥。
// 返回值測(cè)試
function isBigEnough(element, index, array) {
return element >= 10;
}
[12, 5, 8, 130, 44].every(isBigEnough); // false
[12, 54, 18, 130, 44].every(isBigEnough); // true
some() 方法
測(cè)試數(shù)組中是不是至少有1個(gè)元素通過了被提供的函數(shù)測(cè)試。它返回一個(gè)布爾值蜂怎。該方法會(huì)依次執(zhí)行數(shù)組的每個(gè)元素穆刻,如果有一個(gè)元素滿足條件,則表達(dá)式返回 true , 剩余的元素不會(huì)再執(zhí)行檢測(cè)杠步。如果沒有滿足條件的元素氢伟,則返回false。some()
不會(huì)對(duì)空數(shù)組進(jìn)行檢測(cè)幽歼。some()
不會(huì)改變?cè)紨?shù)組腐芍。該方法為 ES6
新增方法。常用語法為:array.some(function(currentValue,index,arr),thisValue)
试躏。
-
function(currentValue, index,arr)
:必須。函數(shù)设褐,數(shù)組中的每個(gè)元素執(zhí)行一次callback
函數(shù)颠蕴,直到它找到一個(gè)會(huì)使callback
返回 truthy 的元素。如果發(fā)現(xiàn)了一個(gè)這樣的元素助析,some
方法將會(huì)立即返回true
并結(jié)束執(zhí)行犀被。
currentValue
:必須。當(dāng)前元素的值外冀。
index
:可選寡键。當(dāng)前元素的索引值。
arr
:當(dāng)前元素所屬的數(shù)組對(duì)象雪隧。 -
thisValue
:可選西轩。當(dāng)執(zhí)行回調(diào)函數(shù) callback 時(shí)员舵,用作 this 的值。 - 返回值:布爾值藕畔。數(shù)組中有至少一個(gè)元素通過回調(diào)函數(shù)的測(cè)試就會(huì)返回true马僻;所有元素都沒有通過回調(diào)函數(shù)的測(cè)試返回值才會(huì)為false。
// 檢測(cè)在數(shù)組中是否有元素大于 10
function isBiggerThan10(element, index, array) {
return element > 10;
}
[2, 5, 8, 1, 4].some(isBiggerThan10); // false
[12, 5, 8, 1, 4].some(isBiggerThan10); // true
有沒有發(fā)現(xiàn)
some()
和every()
很相似注服,這兩個(gè)方法的目的都是為了檢測(cè)數(shù)組中的成員是否滿足所給定的條件韭邓,只是檢測(cè)規(guī)則不一致。
大概總結(jié)了一下溶弟,常用的遍歷 API
應(yīng)該就是上面這些.....不知道有沒有遺漏女淑,感覺開發(fā)中主要用到的也就是這些了吧,做個(gè)小總結(jié):
find()
和findIndex()
方法應(yīng)該是對(duì)應(yīng)的辜御。find()
返回?cái)?shù)組中滿足提供的測(cè)試函數(shù)的第一個(gè)元素的值鸭你,findIndex()
返回?cái)?shù)組中滿足提供的測(cè)試函數(shù)的第一個(gè)元素的索引。
map()
和forEach()
方法應(yīng)該是對(duì)應(yīng)的我抠,都是對(duì)數(shù)組進(jìn)行循環(huán)遍歷苇本。不同的是forEach()
返回值為undefined
,而map()
的返回值是一個(gè)由原數(shù)組每個(gè)元素執(zhí)行回調(diào)函數(shù)的結(jié)果組成的新數(shù)組菜拓。
every()
和some()
方法應(yīng)該是對(duì)應(yīng)的瓣窄。這兩個(gè)方法的目的都是為了檢測(cè)數(shù)組中的成員是否滿足所給定的條件,只是every()
需要數(shù)組中所有元素都滿足條件才返回true
纳鼎,some()
只需要數(shù)組中一個(gè)元素滿足條件就返回true
俺夕。
其它常用 API
不知不覺感覺大部分 API
已經(jīng)都寫到了,當(dāng)然還有一些非常常用的 API
贱鄙,例如數(shù)組合并劝贸、數(shù)組排序等,
concat() 方法
用于合并兩個(gè)或多個(gè)數(shù)組逗宁。此方法不會(huì)更改現(xiàn)有數(shù)組映九,而是返回一個(gè)新數(shù)組。常用語法為:array1.concat(array2,array3,...,arrayX)
瞎颗。
-
array2, array3, ..., arrayX
:必需件甥。該參數(shù)可以是具體的值,也可以是數(shù)組對(duì)象哼拔∫校可以是任意多個(gè)。 - 返回值:返回一個(gè)新的數(shù)組倦逐。
// 基礎(chǔ)應(yīng)用
var num1 = [1, 2, 3],
num2 = [4, 5, 6],
num3 = [7, 8, 9];
var nums = num1.concat(num2, num3);
console.log(nums); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
其實(shí)開發(fā)中 concat()
方法用的非常之多譬正,這里延伸拓展用 concat()
進(jìn)行數(shù)組拍平,前面使用 forEach()
和 flat()
都做過數(shù)組拍平,讓我們來看看 concat()
如何實(shí)現(xiàn)的呢~~~
首先使用 concat()
拍平二維數(shù)組曾我,當(dāng)然此方法無法拍平更高維度的數(shù)組粉怕,如下栗子:
let arr = [1, 2, 3, 4, [5, 6], 7]
let newArr = [].concat.apply([], arr)
console.log(newArr) // [1, 2, 3, 4, 5, 6, 7]
小伙伴肯定會(huì)好奇,說好的用 concat()
來實(shí)現(xiàn)二維數(shù)組拍平您单,你用個(gè) apply()
搞的我一臉懵逼斋荞,也沒看懂為什么~~~如果我們直接使用 concat()
也是可以的,但是需要做如下處理:
let newArr = [].concat(1, 2, 3, 4, [5, 6], 7)
console.log(newArr) // [1, 2, 3, 4, 5, 6, 7]
前面說了虐秦,concat()
支持我們傳遞多個(gè)值平酿,可以為數(shù)組,也可以直接為值悦陋,所以如果我們像上面那樣寫就可以直接得到一個(gè)一維數(shù)組蜈彼,但是 concat()
如果直接合并一個(gè)二維數(shù)組得到的結(jié)果其實(shí)還是一個(gè)二維數(shù)組:
let arr = [1, 2, 3, 4, [5, 6], 7]
let newArr = [].concat(arr)
console.log(newArr) // [1, 2, 3, 4, [5, 6], 7]
所以這里我們使用 apply()
中轉(zhuǎn)一下,因?yàn)樗旧斫邮艿膫髦捣绞骄褪菙?shù)組俺驶,然后內(nèi)部會(huì)幫我們進(jìn)行展開幸逆,所以最后會(huì)得到 [].concat(1, 2, 3, 4, [5, 6], 7)
這種結(jié)構(gòu),即達(dá)到我們的需求暮现。那么我們就可以利用這個(gè)特性还绘,加上遞歸來進(jìn)行多維數(shù)組的拍平,如下栗子:
const arr = [1, 2, 3, [4, 5, 6, [7, [8], 9]], 10]
function flatten(arr) {
const isDeep = arr.some(item => item instanceof Array)
// 如果徹底拍平栖袋,所有子元素都不是數(shù)組拍顷,直接返回該數(shù)組
if (!isDeep) {
return arr
}
// 重復(fù)進(jìn)行拍平
const res = [].concat.apply([], arr)
return flatten(res)
}
const newArr = flatten(arr)
console.log(newArr)
reduce() 方法
對(duì)數(shù)組中的每個(gè)元素執(zhí)行一個(gè)由您提供的reducer函數(shù)(升序執(zhí)行),將其結(jié)果匯總為單個(gè)返回值塘幅。該方法為 ES6
新增方法昔案。常用語法為:array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
。
-
function(total,currentValue, index,arr)
:必需电媳。用于執(zhí)行每個(gè)數(shù)組元素的函數(shù)踏揣。
total
:必需。初始值, 或者計(jì)算結(jié)束后的返回值匾乓。
currentValue
:必需捞稿。當(dāng)前元素。
currentIndex
:可選拼缝。當(dāng)前元素的索引括享。
arr
:可選。當(dāng)前元素所屬的數(shù)組對(duì)象珍促。 -
initialValue
:可選。作為第一次調(diào)用 callback函數(shù)時(shí)的第一個(gè)參數(shù)的值剩愧。 如果沒有提供初始值猪叙,則將使用數(shù)組中的第一個(gè)元素。 在沒有初始值的空數(shù)組上調(diào)用 reduce 將報(bào)錯(cuò)。 - 返回值:返回計(jì)算結(jié)果穴翩。
// 基礎(chǔ)應(yīng)用
const arr = [1, 2, 3, 4, 5]
const res = arr.reduce((total, item) => {
return total + item
}, 6)
console.log(res) // 21
說實(shí)話犬第,這個(gè)方法現(xiàn)在經(jīng)常看到一些地方用到芒帕,但是還是有點(diǎn)迷歉嗓,可能是以前沒用過......感覺 MDN 對(duì)于這個(gè)方法講的挺詳細(xì)的,大家可以移步過去瞅瞅背蟆。這里摘兩個(gè)好理解又實(shí)用的栗子:
// 計(jì)算數(shù)組中每個(gè)元素出現(xiàn)的次數(shù)
var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
var countedNames = names.reduce(function (allNames, name) {
if (name in allNames) {
allNames[name]++;
} else {
allNames[name] = 1;
}
return allNames;
}, {});
console.log(names) // { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }
這里其實(shí)主要用了 initialValue
這個(gè)空對(duì)象來實(shí)現(xiàn)疊加鉴分,其實(shí)疊加的步驟如下:
{}
{'Alice': 1}
{ 'Alice': 1, 'Bob': 1}
{ 'Alice': 1, 'Bob': 1, 'Tiff': 1}
{ 'Alice': 1, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }
{ 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }
再來看一個(gè)小栗子,按屬性對(duì) Object
進(jìn)行分類:
var people = [
{ name: 'Alice', age: 21 },
{ name: 'Max', age: 20 },
{ name: 'Jane', age: 20 }
];
function groupBy(arr, property) {
return arr.reduce((acc, obj) => {
let key = obj[property]
if (!acc[key]) {
acc[key] = []
}
acc[key].push(obj)
return acc
}, {})
}
const res = groupBy(people, 'age')
console.log(res)
// {
// 20: [
// { name: 'Max', age: 20 },
// { name: 'Jane', age: 20 }
// ],
// 21: [{ name: 'Alice', age: 21 }]
// }
感覺看著代碼理解起來很容易带膀,但是自己思考和實(shí)現(xiàn)思路可能又轉(zhuǎn)不太過來.........不過感覺這個(gè)方法確實(shí)挺有用的志珍。
from() 方法
從一個(gè)類似數(shù)組或可迭代對(duì)象創(chuàng)建一個(gè)新的,淺拷貝的數(shù)組實(shí)例垛叨。該方法為 ES6 新增方法伦糯。常用語法為:Array.from(object, mapFunction, thisValue)
。
-
Object
:必需嗽元,要轉(zhuǎn)換為數(shù)組的對(duì)象敛纲。 -
mapFunction
: 可選,數(shù)組中每個(gè)元素要調(diào)用的函數(shù)剂癌。 -
thisValue
:可選淤翔,映射函數(shù)(mapFunction)中的 this 對(duì)象。 - 返回值:一個(gè)新的數(shù)組實(shí)例珍手。
該方法用的應(yīng)該算比較多的办铡,我感覺日常開發(fā)中可能主要用于將 arguments
轉(zhuǎn)化成數(shù)組×找回顧將偽數(shù)組轉(zhuǎn)化為數(shù)組的方式:
// 獲取元素結(jié)合寡具,得到一個(gè)偽數(shù)組,有 length 屬性確不能調(diào)用數(shù)組的方法
const img = document.querySelectorAll('img')
console.log(img instanceof Array) // false
// 使用 Array.from
const imgArr = Array.from(img)
console.log(imgArr instanceof Array) // true
// 使用 slice
const imgArr = [].slice.call(img)
console.log(imgArr instanceof Array) // true
// 使用擴(kuò)展運(yùn)算符
const imgArr = [...img]
console.log(imgArr instanceof Array) // true
再來看下 from()
其它基礎(chǔ)用法:
// 從 String 生成數(shù)組
Array.from('foo') // [ "f", "o", "o" ]
// 數(shù)組去重可以使用 new Set()
const set = new Set(['foo', 'bar', 'baz', 'foo'])
Array.from(set) // [ "foo", "bar", "baz" ]
// 傳入第二個(gè)參數(shù) mapFunction
console.log(Array.from([1, 2, 3], x => x + x)) // [2, 4, 6]
toString() 方法
返回一個(gè)字符串稚补,表示指定的數(shù)組及其元素童叠。常用語法為:arr.toString()
。
- 返回值:一個(gè)表示指定的數(shù)組及其元素的字符串课幕。
這個(gè)方法比較簡(jiǎn)單厦坛,但是也比較常用,這里就簡(jiǎn)單羅列一下乍惊,看個(gè)栗子:
const array1 = [1, 2, 'a', '1a']
console.log(array1.toString()) // "1,2,a,1a"
join() 方法
將一個(gè)數(shù)組(或一個(gè)類數(shù)組對(duì)象)的所有元素連接成一個(gè)字符串并返回這個(gè)字符串杜秸。如果數(shù)組只有一個(gè)項(xiàng)目,那么將返回該項(xiàng)目而不使用分隔符润绎。常用語法為:arr.join(separator)
撬碟。
-
separator
:可選诞挨。指定要使用的分隔符。如果省略該參數(shù)呢蛤,則使用逗號(hào)作為分隔符惶傻。 - 返回值:一個(gè)所有數(shù)組元素連接的字符串。如果 arr.length 為0其障,則返回空字符串银室。
這個(gè)方法和 toString()
一樣比較簡(jiǎn)單,也比較常用励翼,這里也簡(jiǎn)單羅列一下蜈敢,看個(gè)栗子:
var a = ['Wind', 'Rain', 'Fire'];
var myVar1 = a.join(); // myVar1的值變?yōu)?Wind,Rain,Fire"
var myVar2 = a.join(', '); // myVar2的值變?yōu)?Wind, Rain, Fire"
var myVar3 = a.join(' + '); // myVar3的值變?yōu)?Wind + Rain + Fire"
var myVar4 = a.join(''); // myVar4的值變?yōu)?WindRainFire"
reverse() 方法
將數(shù)組中元素的位置顛倒,并返回該數(shù)組抚笔。數(shù)組的第一個(gè)元素會(huì)變成最后一個(gè)扶认,數(shù)組的最后一個(gè)元素變成第一個(gè)。該方法會(huì)改變?cè)瓟?shù)組殊橙。常用語法為:arr.reverse()
辐宾。
- 返回值:顛倒后的數(shù)組。
let arr = [1, 2, 3];
arr.reverse();
console.log(arr); // [3, 2, 1]
sort() 方法
用原地算法對(duì)數(shù)組的元素進(jìn)行排序膨蛮,并返回?cái)?shù)組叠纹。默認(rèn)排序順序是在將元素轉(zhuǎn)換為字符串,然后比較它們的UTF-16代碼單元值序列時(shí)構(gòu)建的敞葛。常用語法為:array.sort(sortfunction)
誉察。
-
sortfunction
:可選。規(guī)定排序順序惹谐。必須是函數(shù)持偏。 - 返回值:Array。數(shù)組在原數(shù)組上進(jìn)行排序氨肌,不生成副本鸿秆。
// 注意,如果直接使用 sort() 并不會(huì)得到我們想要的排序結(jié)果
const array1 = [1, 30, 4, 21, 100000]
array1.sort()
console.log(array1) // [1, 100000, 21, 30, 4]
如果你打算使用 sort()
直接進(jìn)行數(shù)字排序怎囚,可能會(huì)大失所望卿叽,因?yàn)樗呐判蛞?guī)則并不是按照數(shù)字大小來的.......此時(shí)我們可以巧妙使用如下方法:
var numbers = [1, 30, 4, 21, 100000]
// 升序排列 箭頭函數(shù)返回值簡(jiǎn)寫
numbers.sort((a, b) => a - b)
console.log(numbers) // [1, 4, 21, 30, 100000]
// 降序排列
numbers.sort((a, b) => b - a)
console.log(numbers) // [100000, 30, 21, 4, 1]
日常開發(fā)中,可能會(huì)碰到要求我們使用商品金額排序或者商品庫存排序或者商家距離排序恳守,模擬演示實(shí)現(xiàn)該功能考婴?
const friuts = [
{
name: 'apple',
price: 18.5,
count: 10,
juli: 10
},
{
name: 'pear',
price: 1.5,
count: 5,
juli: 20
},
{
name: 'banana',
price: 20.5,
count: 20,
juli: 5
},
]
// 對(duì)象排序的 key 值和是否升序還是降序
function sortExp(key, isAsc) {
return function (x, y) {
return (x[key] - y[key]) * (isAsc ? 1 : -1)
}
}
// 按價(jià)格升序
friuts.sort(sortExp('price', true))
console.log(JSON.stringify(friuts))
// 按價(jià)格降序
friuts.sort(sortExp('price', false))
console.log(JSON.stringify(friuts))
// 按庫存升序
friuts.sort(sortExp('count', true))
console.log(JSON.stringify(friuts))
// 按庫存降序
friuts.sort(sortExp('count', false))
console.log(JSON.stringify(friuts))
// 按距離升序
friuts.sort(sortExp('juli', true))
console.log(JSON.stringify(friuts))
// 按距離降序
friuts.sort(sortExp('juli', false))
console.log(JSON.stringify(friuts))
當(dāng)然,排序并不只有 sort排序
催烘,隨便百度都有 冒泡排序
沥阱、插入排序
、希爾排序
等等多種排序方法伊群。這里簡(jiǎn)單羅列兩種考杉。
- 冒泡排序
什么叫冒泡屁使?類似于水中冒泡,較大的數(shù)沉下去奔则,較小的數(shù)慢慢冒起來,假設(shè)從小到大蔽午,即為較大的數(shù)慢慢往后排易茬,較小的數(shù)慢慢往前排。所以冒泡的實(shí)現(xiàn)原理其實(shí)就是:
數(shù)組中有 n 個(gè)數(shù)及老,比較每相鄰兩個(gè)數(shù)抽莱,如果前者大于后者,就把兩個(gè)數(shù)交換位置骄恶;這樣一來食铐,第一輪就可以選出一個(gè)最大的數(shù)放在最后面;那么經(jīng)過 n-1(數(shù)組的 length - 1) 輪僧鲁,就完成了所有數(shù)的排序虐呻。
知道了冒泡的基本原理,那么我們先來摸清楚數(shù)組中相鄰兩個(gè)數(shù)的位置交換如何實(shí)現(xiàn)寞秃,看如下代碼:
// 我們希望 arr 中的 1 和 2 交換位置
const arr = [1, 2]
// 定義中轉(zhuǎn)變量斟叼,將第一個(gè)值賦給它,第二個(gè)值賦給第一個(gè)值春寿,在將中轉(zhuǎn)變量賦給第二個(gè)值從而完成交換朗涩。
let temp = arr[0]
arr[0] = arr[1]
arr[1] = temp
console.log(arr) // [2, 1]
好吧,前奏已經(jīng)鋪墊好绑改,我們先來實(shí)現(xiàn)第一輪谢床,找出數(shù)組中的最大數(shù),并把它放到數(shù)組的最后面厘线。
const arr = [3, 4, 1, 2]
for (let i = 0; i < arr.length - 1; i++) {
// 如果前一個(gè)數(shù)大于后一個(gè)數(shù)识腿,就交換兩個(gè)數(shù)的位置
if (arr[i] > arr[i + 1]) {
let temp = arr[i]
arr[i] = arr[i + 1]
arr[i + 1] = temp
}
}
console.log(arr) // [3, 1, 2, 4]
看上面的代碼,我們已經(jīng)成功完成了第一輪皆的,將最大的數(shù)字 4 放在了最后面覆履。那我們直接將這個(gè)操作重復(fù) n-1
輪( n
為數(shù)組的長(zhǎng)度)。此時(shí)我們?cè)谏厦嫜h(huán)的外層直接在套一層循環(huán)费薄。
const arr = [3, 4, 1, 2]
// 總共會(huì)執(zhí)行的輪數(shù)
for (let j = 0; j < arr.length - 1; j++) {
// 每一輪前后兩個(gè)數(shù)都進(jìn)行比較
for (let i = 0; i < arr.length - 1; i++) {
if (arr[i] > arr[i + 1]) {
let temp = arr[i]
arr[i] = arr[i + 1]
arr[i + 1] = temp
}
}
}
console.log(arr) // [1, 2, 3, 4]
排序成功硝全,有沒有發(fā)現(xiàn)如果將其拆解,其實(shí)理解起來一點(diǎn)都不復(fù)雜......那么還有沒有啥遺留的問題可以優(yōu)化呢楞抡?我們知道在每一輪的比較過程中伟众,我們都已經(jīng)將最大的數(shù)放到了最后,所以我們?cè)趦?nèi)層遍歷的時(shí)候召廷,因?yàn)樽詈竺嬉粋€(gè)數(shù)已經(jīng)是最大的了凳厢,我們是不是不用將最后一個(gè)數(shù)來進(jìn)行比較账胧。并且我們內(nèi)外兩層循環(huán)都要重復(fù)計(jì)算 arr.length
的長(zhǎng)度,但是數(shù)組的長(zhǎng)度本身是固定不會(huì)改變的先紫,我們是否可以把它抽離到循環(huán)的外面賦值給一個(gè)常量從而減少計(jì)算的性能消耗治泥。好的,考慮的不錯(cuò)遮精,你已經(jīng)是一個(gè)優(yōu)秀的 boy
了居夹,于是我們代碼優(yōu)化如下:
const arr = [3, 4, 1, 2, 30, 21, 100000]
// 數(shù)組長(zhǎng)度抽離成常量
const length = arr.length - 1
for (let j = 0; j < length; j++) {
// 這里要根據(jù)外層for循環(huán)的 j,逐漸減少內(nèi)層 for循環(huán)的次數(shù)
for (let i = 0; i < length - j; i++) {
if (arr[i] > arr[i + 1]) {
let temp = arr[i]
arr[i] = arr[i + 1]
arr[i + 1] = temp
}
}
}
console.log(arr) // [1, 2, 3, 4, 21, 30, 100000]
冒泡排序比較穩(wěn)定簡(jiǎn)單本冲,但是因?yàn)閮蓪忧短籽h(huán)准脂,所以一般處理的數(shù)據(jù)量不宜過大。如果遇到大量數(shù)據(jù)的排序可以選擇快速排序檬洞。
- 快速排序
感覺快速排序可以看 阮一峰老師的快排js實(shí)現(xiàn) 狸膏,講的還是很明白的,主要思想為一下三步:
1添怔、選擇數(shù)組中間數(shù)作為基數(shù)湾戳,并從數(shù)組中取出此基數(shù);
2澎灸、準(zhǔn)備兩個(gè)數(shù)組容器,遍歷數(shù)組拦止,逐個(gè)與基數(shù)比對(duì)糜颠,較小的放左邊容器汹族,較大的放右邊容器其兴;
3、遞歸處理兩個(gè)容器的元素元旬,并將處理后的數(shù)據(jù)與基數(shù)按大小合并成一個(gè)數(shù)組榴徐,返回匀归。
整體實(shí)現(xiàn)代碼栗子:
const arr = [2, 4, 3, 4, 6, 3, 2, 5, 6, 2, 3, 6, 5, 4]
function quickSort(arr) {
if (arr.length <= 1) {
return arr
}
// 基準(zhǔn)位置穆端,理論上可以任意選取
let pivotIndex = Math.floor(arr.length / 2)
// 基準(zhǔn)值
let pivot = arr.splice(pivotIndex, 1)[0]
let left = []
let right = []
for (let i = 0; i < arr.length; i++) {
if (arr[i] < pivot) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
// 使用遞歸不斷重復(fù)這個(gè)過程,就可以得到排序后的數(shù)組攒巍。
return quickSort(left).concat([pivot], quickSort(right))
}
const res = quickSort(arr)
console.log(res)
通過阮老師對(duì)快排的文章,確實(shí)簡(jiǎn)單易懂闻坚,感覺大佬果然是大佬鲤氢,膜拜一下~~~當(dāng)然面試中如果碰到排序要求感覺還是可以使用冒泡排序和 sort()
排序西潘,這兩個(gè)比較簡(jiǎn)單喷市,也容易理解......更多算法練習(xí)可以參考 leedcode 威恼。
感覺這篇文章真是大雜燴箫措,整理了好幾天,中間還休了一個(gè)八天的十一假期植酥,出去浪了~~~寫到這里感覺也差不多可以停了友驮,后續(xù)遇到相關(guān)數(shù)組問題在進(jìn)行補(bǔ)充驾锰。如果文中有不對(duì)的地方或者理解有誤的地方歡迎大家提出并指正卸留。每一天都要相對(duì)前一天進(jìn)步一點(diǎn)椭豫,加油I退帧!算柳!