JavaScript數組迭代(遍歷)方法

前言

ES5和ES6中新增了很多東西,對於數組而言,新增了很多迭代方法,讓咱們能夠拋棄for循環,更方便的寫JS代碼。數組

正文

ES5和ES6中新增的的數組迭代方法以下:瀏覽器

  • forEach函數

  • mapthis

  • filterprototype

  • somecode

  • every對象

  • reduce / reduceRight索引

  • find / findIndexip

其中,find / findIndex是ES6新增的,其他都是ES5新增的。因此這些方法對低版本IE都不兼容。
接下來咱們看看這些方法如何使用並在低版本IE進行兼容。element

forEach

forEach方法是這些方法裏面最基本的一個方法,它的做用是對數組的每一個元素執行一次提供的函數
例如:

var arr = [1, 2, 3];

arr.forEach(function (element, index, array) {
  console.log(element, index, array)
})

//output
1 0 [1, 2, 3]
2 1 [1, 2, 3]
3 2 [1, 2, 3]

forEach方法中的callback函數會被依次傳入三個參數:

  • 數組當前項的值

  • 數組當前項的索引

  • 數組對象自己

forEach方法還能夠傳入第二個參數,這個參數是可選的。若是給forEach傳遞了第二個參數,callback函數裏的this將指向這個參數。若是沒有傳入第二個參數,則this指向全局對象(在瀏覽器是爲window),嚴格模式下是undefined

var arr = [1, 2, 3];
var obj = {name: 'zhang'};

arr.forEach(function (element, index, array) {
  console.log(element, index, array, this)
}, obj)

// output
1 0 [1, 2, 3] {name: "zhang"}
2 1 [1, 2, 3] {name: "zhang"}
3 2 [1, 2, 3] {name: "zhang"}

下面咱們用forEach寫一個稍顯完整的例子了,數組求和:

var sum = 0;

[1, 2, 3].forEach(function (element, index, array) {
  console.log(array[index] == element); // true
  sum += item;
});

console.log(sum); // 6

最後咱們對低版本IE進行一下拓展,是這個方法就有更好的兼容性,代碼以下:

Array.prototype.forEach = Array.prototype.forEach || function(fn, context){
  for (var k = 0, length = this.length; k < length; k++) {
    if (typeof fn === "function" && Object.prototype.hasOwnProperty.call(this, k)) {
      fn.call(context, this[k], k, this);
    }
  }
}

map

map方法的做用就是將原數組按照必定的規則映射成一個新的數組。再將其返回,是返回一個新的數組,而不是將原數組直接改變。使用方法和參數都跟forEach類似。
下面是一個數值求平方的例子:

var data = [1, 2, 3];

var arrayOfSquares = data.map(function (element) {
  return element * element;
});

console.log(arrayOfSquares); //[1, 4, 9]

callback須要有return值,若是沒有,就像下面這樣:

var data = [1, 2, 3];

var arrayOfSquares = data.map(function (element) {
  element * element;
});

console.log(arrayOfSquares); // [undefined, undefined, undefined]

最後咱們對map方法進行一下拓展:

Array.prototype.map = Array.prototype.map || function (fn, context) {
  var arr = [];
  if (typeof fn === "function") {
    for (var k = 0, length = this.length; k < length; k++) {      
      arr.push(fn.call(context, this[k], k, this));
    }
  }
  return arr;
};

filter

filter爲「過濾」、「篩選」的意思。指數組filter後,返回過濾後的新數組。用法和參數跟map差很少。
與map方法不一樣的是,filter方法的callback函數須要返回弱等於truefalse的值。若是爲true,則經過,不然,不經過。
例如:

var arr = [0, 1, 2, 3];

var newArr = arr.filter(function (element, index, array) {
  return e;
})

var newArr2 = arr.filter(function (element, index, array) {
  return e>=2; 
})

console.log(newArr); // [1, 2, 3]
console.log(newArr2); // [2, 3]

下面是對filter方法的拓展:

Array.prototype.filter = Array.prototype.filter || function (fn, context) {
  var arr = [];
  if (typeof fn === "function") {
    for (var k = 0, length = this.length; k < length; k++) {
      fn.call(context, this[k], k, this) && arr.push(this[k]);
    }
  }
  return arr;
};

some

some方法是隻要數組中的某個值,符合你給定的判斷條件就返回true;不然,返回false。用法和參數跟前面的方法同樣。
例如:

function isBigEnough(element, index, array) {
  return element >= 4;
}
var passed = [1, 2, 3].some(isBigEnough);
var passed2 = [1, 2, 3, 4].some(isBigEnough);

console.log(passed); // false
console.log(passed2); // true

下面是some方法的拓展:

Array.prototype.some = Array.prototype.some || function (fn, context) {
  var passed = false;
  if (typeof fn === "function") {
       for (var k = 0, length = this.length; k < length; k++) {
      if (passed === true) break;
      passed = !!fn.call(context, this[k], k, this);
    }
  }
  return passed;
};

every

every方法與some方法相對,every方法是數組中的全部值都符合你給定的判斷條件的時候纔會返回true,不然就返回false
例如:

function isBigEnough(element, index, array) {
  return element >= 3;
}
var passed = [2, 3, 4].every(isBigEnough);
var passed2 = [3, 4, 5].every(isBigEnough);

console.log(passed); // false
console.log(passed2); // true

every方法拓展以下:

Array.prototype.every = Array.prototype.every || function (fn, context) {
  var passed = true;
  if (typeof fn === "function") {
    for (var k = 0, length = this.length; k < length; k++) {
      if (passed === false) break;
      passed = !!fn.call(context, this[k], k, this);
    }
  }
  return passed;
};

reduce / reduceRight

reduce / reduceRight 方法比上面的幾種方法要複雜一些;它的語法以下:

array.reduce(callback,[initialValue])

其中callback能夠依次接受四個參數:

  • accumulator上一次調用回調返回的值,或者是提供的初始值(initialValue

  • currentValue數組中正在處理的元素

  • currentIndex數組中正在處理的元素索引,若是提供了initialValue ,從0開始;不然從1開始。

  • array數組對象自己

reduce / reduceRight 方法中,第二個參數(initialValue)是可選的;其值用於第一次調用callback的第一個參數。
咱們先來看一個例子:

var sum = [1, 2, 3].reduce(function(a, b) {
    return a + b;
});
console.log(sum); // 6

下面咱們來看看reduce是如何運行的
例如執行下面這段代碼:

var sum = [0,1,2,3,4].reduce(function(accumulator, currentValue, currentIndex, array){
  console.log(accumulator, currentValue, currentIndex, array)
  return accumulator + currentValue;
});
console.log(sum);

// output
0 1 1 [0, 1, 2, 3, 4]
1 2 2 [0, 1, 2, 3, 4]
3 3 3 [0, 1, 2, 3, 4]
6 4 4 [0, 1, 2, 3, 4]
10

從上面的輸出結果能夠看出callback被調用四次,每次調用的參數和返回值以下表:

callback accumulator currentValue currentIndex array return
第一次調用 0 1 1 [0, 1, 2, 3, 4] 1
第二次調用 1 2 2 [0, 1, 2, 3, 4] 3
第三次調用 3 3 3 [0, 1, 2, 3, 4] 6
第四次調用 6 4 4 [0, 1, 2, 3, 4] 10

上面是沒有傳入第二個參數(initialValue)的狀況,那傳入第二個參數又是怎麼樣的呢?

var sum = [0,1,2,3,4].reduce(function(accumulator, currentValue, currentIndex, array){
  console.log(accumulator, currentValue, currentIndex, array)
  return accumulator + currentValue;
}, 10);
console.log(sum);

// output
10 0 0 [0, 1, 2, 3, 4]
10 1 1 [0, 1, 2, 3, 4]
11 2 2 [0, 1, 2, 3, 4]
13 3 3 [0, 1, 2, 3, 4]
16 4 4 [0, 1, 2, 3, 4]
20

傳入第二個參數後callback調用了五次,每次調用的參數和返回值以下表:

callback accumulator currentValue currentIndex array return
第一次調用 10 0 0 [0, 1, 2, 3, 4] 10
第二次調用 10 1 1 [0, 1, 2, 3, 4] 11
第三次調用 11 2 2 [0, 1, 2, 3, 4] 13
第四次調用 13 3 3 [0, 1, 2, 3, 4] 16
第五次調用 16 4 4 [0, 1, 2, 3, 4] 20

從上面的狀況能夠看出:不提供initialValue ,reduce方法會從索引1的地方開始執行callback方法,跳過第一個索引。提供 initialValue,從索引0開始。
同時,是否提供initialValue對於回調函數第一次執行時,accumulatorcurrentValue的取值有兩種狀況:調用reduce時提供initialValueaccumulator取值爲initialValuecurrentValue取數組中的第一個值;沒有提供initialValueaccumulator取數組中的第一個值,currentValue取數組中的第二個值。

reduceRight與reduce相似,不一樣之處在於它是從最後一個值開始計算的。

那麼咱們該如何拓展一個reduce / reduceRight方法:

Array.prototype.reduce = Array.prototype.reduce || function (callback, initialValue ) {
  var previous = initialValue, k = 0, length = this.length;
  if (typeof initialValue === "undefined") {
    previous = this[0];
    k = 1;
  }
     
  if (typeof callback === "function") {
    for (k; k < length; k++) {
      this.hasOwnProperty(k) && (previous = callback(previous, this[k], k, this));
    }
  }
  return previous;
};


Array.prototype.reduceRight = Array.prototype.reduceRight || function (callback, initialValue ) {
    var length = this.length, k = length - 1, previous = initialValue;
    if (typeof initialValue === "undefined") {
        previous = this[length - 1];
        k--;
    }
    if (typeof callback === "function") {
       for (k; k > -1; k-=1) {          
          this.hasOwnProperty(k) && (previous = callback(previous, this[k], k, this));
       }
    }
    return previous;
  };

find / findIndex

find方法用於找出第一個符合條件的數組成員。它的參數跟forEach方法是同樣的;全部數組成員依次執行回調函數,直到找出第一個返回值爲true的成員,而後返回該成員。若是沒有符合條件的成員,則返回undefined。
例如:

var value = [1, 5, 10, 15].find(function(element, index, array) {
  return element > 9;
});
var value2 = [1, 5, 10, 15].find(function(element, index, array) {
  return element > 20;
});

console.log(value); // 10
console.log(value2); // undefined

findIndex方法和find類似;不過它返回數組中符合條件的元素的索引。若是全部成員都不符合條件,則返回-1。

var value = [1, 5, 10, 15].findIndex(function(element, index, array) {
  return element > 9;
});
var value2 = [1, 5, 10, 15].findIndex(function(element, index, array) {
  return element > 20;
});

console.log(value); // 2
console.log(value2); // -1

對於不支持find / findIndex方法的瀏覽器,咱們能夠本身實現一個:

Array.prototype.find = Array.prototype.find || function (fn, context) {
  if (typeof fn === "function") {
    for (var k = 0, length = this.length; k < length; k++) {
      if (fn.call(context, this[k], k, this)) {
        return this[k];
      }
    }
  }
  return undefined;
};


Array.prototype.findIndex = Array.prototype.findIndex || function (fn, context) {
  if (typeof fn === "function") {
    for (var k = 0, length = this.length; k < length; k++) {
      if (fn.call(context, this[k], k, this)) {
        return k;
      }
    }
  }
  return -1;
};

最後

上面的兼容實現不知道對不對,歡迎你們指正。
參考資料:
https://developer.mozilla.org...

相關文章
相關標籤/搜索