Javascript數組方法(譯)


在JavaScript中,數組可以使用Array構造函數來創建,或使用[]快速創建,這也是首選的方法。數組是繼承自Object的原型,並且他對typeof沒有特殊的返回值,他只返回'object'。

運行[] instanceof Array他會返回ture。雖然結果是這樣,但也有復雜的類數組對象,如字符串或arguments對象,但arguments對象並非是Array的實例,但他卻擁有length屬性,而且他的值是可以被索引的,因此他可以像一個數組那樣被遍歷。

這本文中,我將介紹數組原型的一些方法,並探討每一種方法的用途:
使用.forEach 來做遍歷
使用.some and .every 來斷言
使用 .join and .concat 來合並
使用 .pop, .push, .shift, and .unshift 來操作棧和隊列
使用 .map 映射模型
使用 .filter 來做查詢
使用 .sort 來做排序
使用 .reduce, .reduceRight 來計算
使用 .slice 來復制
.splice 的用途
使用 .indexOf 來查找
in 操作符介紹
.reverse 的用途

使用 .forEach來遍歷
======
這是原生Javascript數組最簡單的方法之一,但他不支持IE6~8。

forEach 會在遍歷數組中的每個元素時就執行一次回調函數,並傳遞三個參數。
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'


這里使用了.join,這是我們還沒有提及到的,但往后我們會再作介紹,在這個例子中,他可以連接數組中的不同元素,可以達到類似字符串拼接的效果。out[0] + '' + out[1] + '' + out[2] + '' + out[n].

我們不能中止forEach循環和拋出異常,在這些情景下,我們可以選擇使用其他可用的方法。


使用 .some和.every來斷言
=====

如果你曾使用過.Net的 enumerables,也許你熟悉.Any(x => x.IsAwesome) and .All(x => x.IsAwesome).
這些方法和.forEach非常相似,他們同樣地給回調函數傳遞了value, index 和array, 你也同樣的可以給他傳入二個可選參數來作為回調函數的上下文。MDN是這樣來描述.some的:

".some方法可以在遍歷數組中的每個元素時執行回調函數,直到回調函數返回true為止,如果這個元素被發現,.some立即返回true。否則.some會返回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

回調函數會在滿足條件value < 10時停止執行。.every也是同樣的道理,但他的短路是發生在回調函數返回false時。


使用 .join and .concat 來合並
=====
.join方法通常會和.concat方法混淆。.join是創建一個通過分隔符來鏈接數組中各元素的字符串,如果不提供分隔符,他就會默認以逗號作用分隔符。.concat通過他的源數組創建一個新的數組。

.concat 可以傳入多個參數: array.concat(val, val2, val3, valn)
.concat 是可以返回一個新的數組
array.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


使用 .pop, .push, .shift, and .unshift 來操作棧和隊列
===
現在大家都知道可以通過.push方法來往數組中添加元素,但你是否知道.push可以傳入多個參數,一次性把多個參數添加到數組后面。如:[].push('a', 'b', 'c', 'd', 'z')

.pop方法和.push方法剛好相反。他將會返回數組中的最后一個元素,並且同時從數組中刪除這個元素。如果數組為空,則會返回void . (undefined)。使用.push和.pop,我可以非常容易地創建一個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 和.shift來創建一個FIFO(fist 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 (或.pop) 可以容易地遍歷數組。

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

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

list
// <- []


使用 .map 映射模型
=========
.map在遍歷數組中的每個元素時執行一次回調函數,並且會返回一個新的數組。回調函數只會對數組的元素索引執行,並不會對已刪除或沒元素的索引而執行。

Array.prototype.map方法和.forEach,.some和.every有着相似的地方:.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)
    }
})


使用 .filter 來做查詢
======

.filter在遍歷數組中的每個元素時執行一次回調函數,並且回調函數返回的true時,他將會保存這當前元素,最后返回一個新數組。回調函數只會對數組的元素索引執行,並不會對已刪除或沒元素的索引而執行。不傳遞到回調函數的元素將會被簡單的忽略,並且不會在新的數組中出現。

[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, '']


使用.sort 來做排序
========

如果不提供回調函數,元素將會通過轉換為字符后並且按照在字典中的順序來排序。例如在字典中"80"在"9"的前面,但如果按數字來排序,則9在80之前。

就像大多數的排序函數一樣,Array.prototype.sort(fn(a,b))可以比較兩個元素。並且會在以下三個情況中返回值。

如果 a 應該出現在 b 之前,則返回值小於0。
如果a 和b 相等,則返回0。
如果 a 應該出現在 b 之后,剛返回值大於0。

[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]


使用 .reduce, .reduceRight 來計算
========
這兩個方法都有着同樣的特性:
.reduce(callback(previousValue, currentValue, index, array), initialValue).
在每一個回調函數執行時,previousValue將會被返回。初始化時initialValue將會被傳入回調函數,currentValue包含着當前的元素,index表示該元素的在數組中的位置。array為數組的引用。
一個經典的.reduce例子就是加法函數。

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

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


如果說我們要合並一些字符串,我們可能會用到.join方法來達到目的。但是在下面這個例子中,.join方法就可能達不到我們的要求了,除非這些對象都有valueOf或者toString屬性。但我們可以使用.reduce方法來輕松實現合並各對象為字符串。

function concat (input) {
    return input.reduce(function (partial, value) {
        if (partial) {
            partial += ', '
        }
        return partial + value.name
    }, '')
}

concat([
    { name: 'George' },
    { name: 'Sam' },
    { name: 'Pear' }
])

注:reduce和reduceRight的區別是,reduce 是從數組左到右開始遍歷,而reduceRight是從數組右到左開始遍歷。


使用 .slice 來復制
======
Array.prototype.slice can be used to convert array-like objects into real arrays.
和.concat相似,可以通過不傳遞參數給.slice方法來復制源數組。.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 }]


除此之外,我們還可以把類數組轉換為真正的數組之后,並且去除數組前面幾個元素。

function format (text, bold) {
    if (bold) {
        text = '<b>' + text + '</b>'
    }
    var values = Array.prototype.slice.call(arguments, 2)

    values.forEach(function (value) {
        text = text.replace('%s', value)
    })

    return text
}

format('some%sthing%s %s', true, 'some', 'other', 'things')
// <- <b>somesomethingother things</b>


.splice 的用途
====
.splice也是一個常用的數組方法。你可以通過.splice來刪除元素,插入新元素,並且可以調用一次.splice在同樣的位置達到刪除,插入元素的目標不。要注意的是,這個方法是會改變源數組。

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]


使用 .indexOf 來查找
=========
通過使用.indexOf方法,我們可以找到元素在數組中的位置。如果找不到,將會返回-1。如果要查找,通常我會這樣寫比較a === 'a' || a === 'b' || a === 'c',但在這個場景,你可以['a', 'b', 'c'].indexOf(a) !== -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。

in 操作符介紹
========
.indexOf 和 in操作符非常容易混淆。

var a = [1, 2, 5]

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

5 in a
// <- false

問題就在這里,in操作符是用於檢查一個對象的鍵,而不是查找一個元素在數組中的位置。當然這比.indexOf快。

var a = [3, 7, 6]

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

in操作符相乎是把傳遞進來的值轉換為布爾值。!!表達式是可以把值隱式轉換為布爾值。

關於.reverse
=========
這個方法是可以把數組中的元素位置反轉。

var a = [1, 1, 7, 8]

a.reverse()
// [8, 7, 1, 1]

這里並非返回一個副本,而是直接修改了數組的本身。


譯自:
http://flippinawesome.org/2013/11/25/fun-with-javascript-native-array-functions/



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM