JavaScript原生函數(shù)之——Array

目錄

  1. 數(shù)組的本質(zhì)
  2. Array構(gòu)造方法
  3. Array實(shí)例方法
    • join()
    • concat()
    • reverse()
    • slice()
    • splice()
    • sort()
    • map()
    • forEach()
    • filter()
    • some() 和 every()
    • reduce() 和 reduceRight()
  4. 總結(jié)

數(shù)組的本質(zhì)

在講Js原生函數(shù)Array之前,我們先探討一下數(shù)組的本質(zhì)。先給出結(jié)論:數(shù)組是一個原型鏈上包含Array.prototype的對象。

這就是區(qū)分?jǐn)?shù)組和偽數(shù)組的唯一標(biāo)志。常見的偽數(shù)組,比如:

  • arguments 對象
  • document.querySelectAll('div') 返回的對象
  • 字符串

它們都有與數(shù)組幾乎一樣的屬性,可以造出來 0,1,2,3,4,5...n,length 這些 key,能循環(huán)遍歷,能自己改寫valueOftoString方法來實(shí)現(xiàn)數(shù)組差不多的功能,等等。但是它們無法直接調(diào)用Array.prototype中的方法,比如pushpop,shift,unshift……因?yàn)樗鼈兪荗bject構(gòu)造出來的,原型鏈上沒有這些方法。

要想調(diào)用Array.prototype中的方法,兩條路:

  1. 使用數(shù)組的slice方法將“類似數(shù)組的對象”變成真正的數(shù)組
  2. Array.prototype.join.call(arrayLike, ',')join只是其中一種方法,調(diào)用比如forEach可在第二個參數(shù)傳入自定義的print方法來實(shí)現(xiàn)遍歷輸出)

以上的兩種方法中,第二種的效率比直接使用原生數(shù)組的forEach要慢,建議第一種。

其次,數(shù)組的length是一個動態(tài)屬性,等于鍵名中的最大整數(shù)加上1;而偽數(shù)組的length并不會隨著值的添加而改變,需要自己設(shè)置。

另外數(shù)組也可以添加非數(shù)字值的key,比如各種奇怪的字符串。和狹義的對象一樣,數(shù)組的鍵名其實(shí)也是字符串。之所以可以用數(shù)值讀取,是因?yàn)榉亲址逆I名會被轉(zhuǎn)為字符串。也就是說數(shù)組本質(zhì)是對象的一種,由typeof的返回值也可見。

Array構(gòu)造方法

Array是 JavaScript 的原生對象,同時也是一個構(gòu)造函數(shù),可以用它生成新的數(shù)組。

(1)一個參數(shù)

由上圖可見,當(dāng)只傳入一個3的時候,表示數(shù)組的length為3,而數(shù)組內(nèi)是empty × 3,雖然a[0]返回undefined,但是數(shù)組中并沒有'0'這個key。

另外,對于復(fù)雜類型,有new沒有new一樣,只是語義上的區(qū)別,沒有new表示封裝為對象,有new表示生成一個對象。基本類型有new表示生成對象,沒有new還是原來的類型,僅取值。

var arr = Array(3)
typeof arr  // "object"

var a = String('123')
typeof a    // "string"

var b = new String('123')
typeor b    //  "object"

傳入非正整數(shù)數(shù)字參數(shù),會有其他結(jié)果:

// 非正整數(shù)的數(shù)值作為參數(shù),會報錯
new Array(3.2) // RangeError: Invalid array length
new Array(-3) // RangeError: Invalid array length

// 單個非數(shù)值(比如字符串、布爾值、對象等)作為參數(shù),
// 則該參數(shù)是返回的新數(shù)組的成員
new Array('abc') // ['abc']
new Array([1]) // [Array[1]]

(2)多個參數(shù)

var arr = new Array(3, 3)
arr  // [3, 3]

傳入多個參數(shù)時,所有參數(shù)都是返回的新數(shù)組的成員。

可以看到,Array作為構(gòu)造函數(shù),行為很不一致。因此,不建議使用它生成新數(shù)組,直接使用數(shù)組字面量是更好的做法。

Array實(shí)例方法

實(shí)例方法是定義在Array.prototype上的方法,每個實(shí)例對象都會繼承到。
常用的:

  • valueOf:返回?cái)?shù)組本身
  • toString:返回?cái)?shù)組的字符串形式(二維及以上會展開)
  • indexOf:返回給定元素在數(shù)組中第一次出現(xiàn)的位置,如果沒有出現(xiàn)則返回-1
  • push:用于在數(shù)組的末端添加一個或多個元素,并返回添加新元素后的length
  • pop:用于刪除數(shù)組的最后一個元素,并返回該元素
  • shift:用于刪除數(shù)組的第一個元素,并返回該元素
  • unshift:用于在數(shù)組的第一個位置添加一個或多個元素,并返回添加新元素后的數(shù)組長度

注意:以上四種增刪數(shù)組的方法均會改變原數(shù)組。

join()

join()方法以指定參數(shù)作為分隔符,將所有數(shù)組成員連接為一個字符串返回。如果不提供參數(shù),默認(rèn)用逗號分隔。

var a = [1, 2, 3, 4];

a.join(' ') // '1 2 3 4'
a.join(' | ') // "1 | 2 | 3 | 4"
a.join() // "1,2,3,4"

['a',, 'b'].join('-')
// 'a--b'  
//  空位轉(zhuǎn)成空字符串

concat()

concat方法用于多個數(shù)組的合并。它將新數(shù)組的成員,添加到原數(shù)組成員的后部,然后返回一個新數(shù)組,原數(shù)組不變。

var a = ['hello']
a.concat(['world'], ['!'])  // ["hello", "world", "!"]
a  // ['hello']

數(shù)組的淺拷貝:

var a = [{'n': 1}, {'s': 'abc'}]
var newArr = a.concat()

a[0].n = 2
newArr[0].n  // 2

reverse()

reverse方法用于顛倒排列數(shù)組元素,返回改變后的數(shù)組。注意,該方法將改變原數(shù)組

slice()

slice,即“切片”,該方法用于提取目標(biāo)數(shù)組的一部分,返回一個新數(shù)組,原數(shù)組不變。
可傳入兩個參數(shù),第一個參數(shù)為起始位置,第二個參數(shù)為終止位置(但該位置的元素本身不包括在內(nèi))

var a = ['a', 'b', 'c']

//  起始位置是1,末尾位置省略,表示直到末尾
a.slice(1) // ["b", "c"]

a.slice(1, 2) // ["b"]

//  第二個參數(shù)超過length也可以
a.slice(2, 6) // ["c"]

//  等于返回一個原數(shù)組的拷貝
a.slice() // ["a", "b", "c"]

//  負(fù)數(shù)表示倒數(shù)計(jì)算的位置
a.slice(-2) // ["b", "c"]

//  注意這里不包括-1位置的元素
a.slice(-2, -1) // ["b"]

//  如果第一個參數(shù)大于等于數(shù)組長度,或者第二個參數(shù)小于第一個參數(shù),則返回空數(shù)組
a.slice(4) // []
a.slice(2, 1) // []

splice()

splice方法用于刪除原數(shù)組的一部分成員,并可以在刪除的位置添加新的數(shù)組成員,返回值是被刪除的元素。注意,該方法會改變原數(shù)組。

語法:

arr.splice(start, count, addElement1, addElement2, ...)

start起始位置,count刪掉幾個元素,之后的全是添加的新元素。

var a = ['a', 'b', 'c', 'd', 'e', 'f']

a.splice(4, 2, 1, 2) // ["e", "f"]
a // ["a", "b", "c", "d", 1, 2]

a.splice(-4, 2)  // ["c", "d"]
a  // ["a", "b", 1, 2]

//  count設(shè)置為0,就表示只添加元素
a.splice(2, 0, "c")  // []
a  // ["a", "b", "c", 1, 2]

//  只設(shè)置start,就表示從start開始切分為兩個數(shù)組
a.splice(3)  //  [1, 2]
a  // ["a", "b", "c"]

sort()

sort方法對數(shù)組成員進(jìn)行排序,默認(rèn)是按照字典順序排序,底層原理是快排。排序后,原數(shù)組將被改變

[10111, 1101, 111].sort()    // [10111, 1101, 111]

如果想讓sort方法按照自定義方式排序,可以傳入一個函數(shù)作為參數(shù)。

[
  { name: "張三", age: 30 },
  { name: "李四", age: 24 },
  { name: "王五", age: 28  }
].sort(function (o1, o2) {
  return o1.age - o2.age;  //  表示按年紀(jì)從小到大,反之是從大到小
})
// [
//   { name: "李四", age: 24 },
//   { name: "王五", age: 28  },
//   { name: "張三", age: 30 }
// ]

map()

map方法將數(shù)組的所有成員依次傳入?yún)?shù)函數(shù),然后把每一次的執(zhí)行結(jié)果組成一個新數(shù)組返回。該函數(shù)調(diào)用時,map方法向它傳入三個參數(shù):當(dāng)前成員、當(dāng)前位置和數(shù)組本身??梢赃x擇性省略參數(shù)。

[1, 2, 3].map(function(elem, index, arr) {
  return elem * index;
})
// [0, 2, 6]

map()可以非常簡潔的實(shí)現(xiàn)一些小功能,比如:

// 所有數(shù)字取平方
[1, 2, 3].map(v => v ** 2)    // [1, 4, 9]  

// 所有數(shù)字轉(zhuǎn)字符串
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
arr.map(String)     // ['1', '2', '3', '4', '5', '6', '7', '8', '9']

map方法不會跳過undefinednull,但是會跳過空位。

forEach()

forEach方法與map方法很相似,也是對數(shù)組的所有成員依次執(zhí)行參數(shù)函數(shù)。但是,forEach方法不返回值,只用來操作數(shù)據(jù)。
forEach的用法與map方法一致,參數(shù)是一個函數(shù),該函數(shù)同樣接受三個參數(shù):當(dāng)前值、當(dāng)前位置、整個數(shù)組。

[2, 5, 9].forEach(function(elem, index, arr){
    console.log('[' + index + '] = ' + element)
})
// [0] = 2
// [1] = 5
// [2] = 9

注意,forEach方法無法中斷執(zhí)行,總是會將所有成員遍歷完。如果希望符合某種條件時,就中斷遍歷,要使用for循環(huán)。

forEach方法不會跳過undefinednull,但會跳過空位。

filter()

filter方法用于過濾數(shù)組成員,滿足條件的成員組成一個新數(shù)組返回。
它的參數(shù)是一個函數(shù),所有數(shù)組成員依次執(zhí)行該函數(shù),返回結(jié)果為true的成員組成一個新數(shù)組返回。該方法不會改變原數(shù)組。也就是說,這個函數(shù)就是過濾規(guī)則。

[1, 2, 3, 4, 5].filter(v => v%2 === 0)    // 篩選偶數(shù)

filter方法的參數(shù)函數(shù)可以接受三個參數(shù):當(dāng)前成員,當(dāng)前位置和整個數(shù)組。

some() 和 every()

這兩個方法類似“斷言”(assert),返回一個布爾值,表示判斷數(shù)組成員是否符合某種條件。

它們接受一個函數(shù)作為參數(shù),所有數(shù)組成員依次執(zhí)行該函數(shù)。該函數(shù)接受三個參數(shù):當(dāng)前成員、當(dāng)前位置和整個數(shù)組,然后返回一個布爾值。

some方法是只要一個成員的返回值是true,則整個some方法的返回值就是true,否則返回falseevery方法是所有成員的返回值都是true,整個every方法才返回true,否則返回false。有點(diǎn) ||&& 的意思。

var arr = [1, 2, 3, 4, 5]
arr.some(v => v >= 3)  // true
arr.every(v => v >= 3)  // false

注意,對于空數(shù)組,some方法返回falseevery方法返回true,回調(diào)函數(shù)都不會執(zhí)行。

reduce() 和 reduceRight()

reduce方法和reduceRight方法依次處理數(shù)組的每個成員,最終累計(jì)為一個值。它們的差別是,reduce是從左到右處理(從第一個成員到最后一個成員),reduceRight則是從右到左(從最后一個成員到第一個成員),其他完全一樣。

reduce方法和reduceRight方法的第一個參數(shù)都是一個函數(shù)。該函數(shù)接受以下四個參數(shù):

  1. 累積變量,默認(rèn)為數(shù)組的第一個成員
  2. 當(dāng)前變量,默認(rèn)為數(shù)組的第二個成員
  3. 當(dāng)前位置(從0開始)
  4. 原數(shù)組

這四個參數(shù)之中,只有前兩個是必須的,后兩個則是可選的。

如果要對累積變量指定初值,可以把它放在reduce方法和reduceRight方法的第二個參數(shù)。

例子:找出字符長度最長的數(shù)組成員

function findLongest(entries) {
  return entries.reduce(function (longest, entry) {
    return entry.length > longest.length ? entry : longest
  }, '');
}

findLongest(['aaa', 'bb', 'c']) // "aaa"

例子:計(jì)算所有奇數(shù)的和

var a = [1,2,3,4,5,6,7,8,9]
a.reduce((sum, n) => n % 2 === 1 ? sum + n : sum)    // 25

總結(jié)

原生函數(shù)Array作為Js的標(biāo)準(zhǔn)庫之一,其API非常重要,記住常用的API能讓我們事半功倍地實(shí)現(xiàn)很多功能。并且,上面這些數(shù)組方法之中,有不少返回的還是數(shù)組,所以可以鏈?zhǔn)绞褂?,比如篩選數(shù)據(jù)庫中的email并且遍歷輸出,等等。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容