1. 程式人生 > 程式設計 >JavaScript原生陣列函式例項彙總

JavaScript原生陣列函式例項彙總

在JavaScript中,建立陣列可以使用Array建構函式,或者使用陣列直接量[],後者是首選方法。Array物件繼承自Object.prototype,對陣列執行typeof操作符返回object而不是array。然而,[] instanceof Array也返回true。也就是說,類陣列物件的實現更復雜,例如strings物件、arguments物件,arguments物件不是Array的例項,但有length屬性,並能通過索引取值,所以能像陣列一樣進行迴圈操作。

在本文中,我將複習一些陣列原型的方法,並探索這些方法的用法。

  • 迴圈:.forEach
  • 判斷:.some和.every
  • 區分.join和.concat
  • 棧和佇列的實現:.pop,.push,.shift,和 .unshift
  • 模型對映:.map
  • 查詢:.filter
  • 排序:.sort
  • 計算:.reduce和.reduceRight
  • 複製:.slice
  • 強大的.splice
  • 查詢:.indexOf
  • 操作符:in
  • 走近.reverse

迴圈:.forEach

這是JavaScript中最簡單的方法,但是IE7和IE8不支援此方法。

.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中的列舉,這兩個方法和.Any(x => x.IsAwesome) 、 .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,array) {
  if (value > max) max = value
  return value < 10
})
console.log(max)
// <- 12
satisfied
// <- true

注意,當回撥函式的value < 10時,中斷函式迴圈。.every的執行原理和.some類似,但回撥函式是返回false而不是true。

區分.join和.concat

.join和.concat 經常混淆。.join(separator)以separator作為分隔符拼接陣列元素,並返回字串形式,如果沒有提供separator,將使用預設的,。.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,.shift和 .unshift

每個人都知道.push可以再陣列末尾新增元素,但是你知道可以使用[].push(‘a',‘b',‘c',‘d',‘z')一次性新增多個元素嗎?

.pop 方法是.push 的反操作,它返回被刪除的陣列末尾元素。如果陣列為空,將返回void 0 (undefined),使用.pop和.push可以建立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,3)
stack.next()
// <- 3
相反,可以使用.shift和 .unshift建立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,3)
queue.next()
// <- 1
Using .shift (or .pop) is an easy way to loop through a set of array elements,while draining the array in the process.
list = [1,4,6,7,9,10]
while (item = list.shift()) {
  console.log(item)
}
list
// <- []

模型對映:.map

.map為陣列中的每個元素提供了一個回撥方法,並返回有呼叫結果構成的新陣列。回撥函式只對已經指定值的陣列索引執行;它不會對已刪除的或未指定值的元素呼叫。

Array.prototype.map 和上面提到的.forEach、.some和 .every有相同的引數格式:.map(fn(value,array),thisArgument)

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

undefined × 3很好地解釋了.map不會對已刪除的或未指定值的元素呼叫,但仍然會被包含在結果陣列中。.map在建立或改變陣列時非常有用,看下面的示例:

// casting
[1,'2','30','9'].map(function (value) {
  return parseInt(value,10)
})
// 1,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的元素組成的新陣列。回撥函式只會對已經指定值的陣列項呼叫。

通常用法:.filter(fn(value,thisArgument),跟C#中的LINQ表示式和SQL中的where語句類似,.filter只返回在回撥函式中返回true值的元素。

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

排序:.sort(compareFunction)

如果沒有提供compareFunction,元素會被轉換成字串並按照字典排序。例如,”80″排在”9″之前,而不是在其後。
跟大多數排序函式類似,Array.prototype.sort(fn(a,b))需要一個包含兩個測試引數的回撥函式,其返回值如下:

  • a在b之前則返回值小於0
  • a和b相等則返回值是0
  • a在b之後則返回值小於0

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

計算:.reduce和.reduceRight

這兩個函式比較難理解,.reduce會從左往右遍歷陣列,而.reduceRight則從右往左遍歷陣列,二者典型用法:.reduce(callback(previousValue,currentValue,initialValue)。

previousValue 是最後一次呼叫回撥函式的返回值,initialValue則是其初始值,currentValue是當前元素值,index是當前元素索引,array是呼叫.reduce的陣列。

一個典型的用例,使用.reduce的求和函式。

Array.prototype.sum = function () {
return this.reduce(function (partial,value) {
return partial + value
},0)
};
[3,10].sum()
// <- 28

如果想把陣列拼接成一個字串,可以用.join實現。然而,若陣列值是物件,.join就不會按照我們的期望返回值了,除非物件有合理的valueOf或toString方法,在這種情況下,可以用.reduce實現:

function concat (input) {
  return input.reduce(function (partial,value) {
    if (partial) {
      partial += ','
    }
    return partial + value
  },'')
}
concat([
  { name: 'George' },{ name: 'Sam' },{ name: 'Pear' }
])
// <- 'George,Sam,Pear'

複製:.slice

和.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',length: 2 })
// <- [{ 0: 'a',length: 2 }]

此外,.slice的另一個通常用法是從一個引數列表中刪除一些元素,這可以將類陣列物件轉換為真正的陣列。

function format (text,bold) {
  if (bold) {
    text = '' + text + ''
  }
  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')

強大的.splice

.splice 是我最喜歡的原生陣列函式,只需要呼叫一次,就允許你刪除元素、插入新的元素,並能同時進行刪除、插入操作。需要注意的是,不同於`.concat和.slice,這個函式會改變源陣列。

var source = [1,11,13]
var spliced = source.splice(3,7)
console.log(source)
// <- [1,13]
spliced
// <- [8,8]

正如你看到的,.splice會返回刪除的元素。如果你想遍歷已經刪除的陣列時,這會非常方便。

var source = [1,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,9]

查詢:.indexOf

利用.indexOf 可以在陣列中查詢一個元素的位置,沒有匹配元素則返回-1。我經常使用.indexOf的情況是當我有比較時,例如:a === ‘a' || a === ‘b' || a === ‘c',或者只有兩個比較,此時,可以使用.indexOf:['a','b','c'].indexOf(a) !== -1。

注意,如果提供的引用相同,.indexOf也能查詢物件。第二個可選引數用於指定開始查詢的位置。

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,5]
1 in a
// <- true,but because of the 2!
5 in a
// <- false

問題是in操作符是檢索物件的鍵而非值。當然,這在效能上比.indexOf快得多。

var a = [3,6]
1 in a === !!a[1]
// <- true

走近.reverse

該方法將陣列中的元素倒置。

var a = [1,1,8]
a.reverse()
// [8,1]

.reverse 會修改陣列本身。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。