JavaScript中原生Array數組方法詳解

JS中,數組可以通過 Array 構造函數或 [] 字面量的方式創建。數組是一個特殊的對象,繼承自 Object 原型,但用 typeof 判斷時,並沒有一個特定的值,仍然返回 'object'。但使用 [] instanceof Array 返回 true。這說明,JS中存在一個類數組的對象,就像字符串對象或 arguments 對象。arguments 對象不是數組的實例,但仍然有 length 屬性,值也可以被索引,因此也可以像數組一樣被循環遍歷。

這篇文章中,我將盤點下定義在 Array 原型上的一些常用的方法,並逐一介紹其用法。

  • 循環 .forEach
  • 斷言 .some,.every
  • 連接與合併 .join,.concat
  • 棧與隊列 .pop, .push, .shift, .unshift
  • 模型映射 .map
  • 過濾 .filter
  • 排序 .sort
  • 聚合 .reduce, .reduceRight
  • 截取 .slice
  • 刪除插入 .splice
  • 查找值 .indexOf
  • 查找鍵 in 操作符
  • 顛倒 .reverse

native-array-functions

1.forEach()

forEach 對數組的所有成員依次執行且只執行一次參數函數,參數函數包含以下3個參數:

  • value 當前值
  • index 當前位置,索引值
  • array 當前數組

進一步,可以傳遞一個可選參數用於指定函數裏的 this 上下文。

['_', 't', 'a', 'n', 'i', 'f', ']'].forEach(function (value, index, array) {
    this.push(String.fromCharCode(value.charCodeAt() + index + 2))
}, out = [])

out.join('')
// <- 'awesome'

forEach的缺陷是既不能中斷循環,也不能拋出異常。

2.some(), every()

這兩個方法類似“斷言”(assert),返回一個布爾值,表示判斷數組成員是否符合某種條件。
類似於 forEach,同樣接受一個包含 value, index, and array的回調函數作爲參數,而且也可以指定 上下文中的this。
some方法是隻要一個成員的返回值是true,則整個some方法的返回值就是true,否則返回false。
every方法是所有成員的返回值都是true,整個every方法才返回true,否則返回false。

max = -Infinity
satisfied = [10, 12, 10, 8, 5, 23].some(function (value, index, array) {
    if (value > max) max = value
    return value < 10
})

console.log(max)
// <- 12

satisfied
// <- true

3.join() and concat()

這兩個方法容易搞混,join是將數組內各個成員用分隔符連接成一個字符串,分隔符默認是 ,,concat用於多個數組的合併。它將新數組的成員,添加到原數組成員的後部,然後返回一個新數組,原數組不變。

var a = { foo: 'bar' }
var b = [1, 2, 3, a]
var c = b.concat()

console.log(b === c)
// <- false

b[3] === a && c[3] === a
// <- true

4.pop, .push, .shift, and .unshift

每個人都知道往數組末尾添加一個元素是用push方法,但你知道可以一次性添加多個元素嗎,像這樣 [].push('a', 'b', 'c', 'd', 'z').
pop是push方法的逆操作,去除數組最後一個元素同時返回這個元素,如果是空數組則返回 undefined,使用這兩個方法很容易實現 LIFO(last in first out) 棧結構。

function Stack () {
    this._stack = []
}

Stack.prototype.next = function () {
    return this._stack.pop()
}

Stack.prototype.add = function () {
    return this._stack.push.apply(this._stack, arguments)
}

stack = new Stack()
stack.add(1,2,3)

stack.next()
// <- 3

相應的,我們可以通過 .unshift and .shift 實現一個 FIFO (first in first out)隊列。

function Queue () {
    this._queue = []
}

Queue.prototype.next = function () {
    return this._queue.shift()
}

Queue.prototype.add = function () {
    return this._queue.unshift.apply(this._queue, arguments)
}

queue = new Queue()
queue.add(1,2,3)

queue.next()
// <- 1

使用 .shift (or .pop) 很容易用while循環清空一個數組,如下:

list = [1,2,3,4,5,6,7,8,9,10]

while (item = list.shift()) {
    console.log(item)
}

list
// <- []

5.map()

map方法將數組的所有成員依次傳入回調函數,然後把每一次的執行結果組成一個新數組返回。
方法簽名類似於 forEach,.map(fn(value, index, array), thisArgument).

values = [void 0, null, false, '']
values[7] = void 0
result = values.map(function(value, index, array){
    console.log(value)
    return value
})
// <- [undefined, null, false, '', undefined × 3, undefined]

undefined × 3 表明了回調函數不會在已刪除或未賦值的成員執行,而是自己返回包含在返回數組中。Map在映射或轉換數組時非常有用,比如下面這個例子:

// casting
[1, '2', '30', '9'].map(function (value) {
    return parseInt(value, 10)
})
// 1, 2, 30, 9

[97, 119, 101, 115, 111, 109, 101].map(String.fromCharCode).join('')
// <- 'awesome'

// a commonly used pattern is mapping to new objects
items.map(function (item) {
    return {
        id: item.id,
        name: computeName(item)
    }
})

6.filter()

filter方法用於過濾數組成員,滿足條件的成員組成一個新數組返回。

使用類似於,.filter(fn(value, index, array), thisArgument).

[void 0, null, false, '', 1].filter(function (value) {
    return value
})
// <- [1]

[void 0, null, false, '', 1].filter(function (value) {
    return !value
})
// <- [void 0, null, false, '']

7.sort(compareFunction)

sort方法對數組成員進行排序,默認是按照字典順序排序。排序後,原數組將被改變。
像大多數的排序函數一樣,Array.prototype.sort(fn(a,b)) 接受一個回調函數用於比較 a,b 的大小,而且爲下面三種情況之一:

  • return value < 0 if a comes before b
  • return value === 0 if both a and b are considered equivalent
  • return value > 0 if a comes after b
[9,80,3,10,5,6].sort()
// <- [10, 3, 5, 6, 80, 9]

[9,80,3,10,5,6].sort(function (a, b) {
    return a - b
})
// <- [3, 5, 6, 9, 10, 80]

8.reduce(), reduceRight()

Reduce函數一開始很難理解,這些函數會循環數組,從左向右 (.reduce) 或從右向左 (.reduceRight),每次回調函數執行時會接受之前的部分結果,最終返回值是單個聚合的值。

兩個方法具有相同的語法:

reduce(callback(previousValue, currentValue, index, array), initialValue)

previousValue 是上次回調函數執行的返回值,或者第一次執行時的初始值;currentValue 是當前值;index 是當前值位置;array 是執行reduce方法數組的引用;initialValue 是初始值;

.reduce方法的一個典型應用場景是數組成員求和:

Array.prototype.sum = function () {
    return this.reduce(function (partial, value) {
        return partial + value
    }, 0)
};

[3,4,5,6,10].sum()
// <- 28

9.slice()

slice()方法用於提取目標數組的一部分,返回一個新數組,原數組不變。

arr.slice(start, end)

它的第一個參數爲起始位置(從0開始,會包括在返回的新數組之中),第二個參數爲終止位置(但該位置的元素本身不包括在內)。如果省略第二個參數,則一直返回到原數組的最後一個成員。

與concat()類似,slice()如果沒有任何參數則返回原數組的淺拷貝。Array.prototype.slice可以用來將類數組的對象轉換成真正的數組。

Array.prototype.slice.call({ 0: 'a', 1: 'b', length: 2 })
// <- ['a', 'b']

concat做不到,它會返回一個包含傳入對象的數組。

Array.prototype.concat.call({ 0: 'a', 1: 'b', length: 2 })
// <- [{ 0: 'a', 1: 'b', length: 2 }]

10.splice()

.splice 是我最喜歡的array原生數組方法,它允許你在一次性在同一個位置刪除、插入元素,注意:這個函數會改變原數組。

var source = [1,2,3,8,8,8,8,8,9,10,11,12,13]
var spliced = source.splice(3, 4, 4, 5, 6, 7)

console.log(source)
// <- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ,13]

spliced
// <- [8, 8, 8, 8]

你可能注意到了,它會返回被刪除的元素,這個常用於循環被刪除的元素,你之前可能忘記了。

var source = [1,2,3,8,8,8,8,8,9,10,11,12,13]
var spliced = source.splice(9)

spliced.forEach(function (value) {
    console.log('removed', value)
})
// <- removed 10
// <- removed 11
// <- removed 12
// <- removed 13

console.log(source)
// <- [1, 2, 3, 8, 8, 8, 8, 8, 9]

11.indexOf()、lastIndexOf()

indexOf方法返回給定元素在數組中第一次出現的位置,如果沒有出現則返回-1。

var a = { foo: 'bar' }
var b = [a, 2]

console.log(b.indexOf(1))
// <- -1

console.log(b.indexOf({ foo: 'bar' }))
// <- -1

console.log(b.indexOf(a))
// <- 0

console.log(b.indexOf(a, 1))
// <- -1

b.indexOf(2, 1)
// <- 1

lastIndexOf方法返回給定元素在數組中最後一次出現的位置,如果沒有出現則返回-1。

注意,這兩個方法不能用來搜索NaN的位置,即它們無法確定數組成員是否包含NaN。

這是因爲這兩個方法內部,使用嚴格相等運算符(===)進行比較,而NaN是唯一一個不等於自身的值。

12.in 操作符

in 用於查找 key 在一個數組中是否存在,indexOf則是查找值。當然速度快於 indexOf.

var a = [1, 2, 5]

1 in a
// <- true, but because of the 2!

5 in a
// <- false
var a = [3, 7, 6]

1 in a === !!a[1]
// <- true

in 操作符跟把相應位置的值轉化爲布爾值類似。!! 表達式常用於將一個值取反然後再取反,這是一種高效的將真值轉爲布爾值的方式。

13.reverse()

reverse方法用於顛倒排列數組元素,返回改變後的數組。注意,該方法將改變原數組。

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

a.reverse() // ["c", "b", "a"]

a // ["c", "b", "a"]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章