淺談JS的數組遍歷方法

用過Underscore的朋友都知道,它對數組(集合)的遍歷有着很是完善的API能夠調用的,_.each()就是其中一個。下面就是一個簡單的例子:
var arr = [1, 2, 3, 4, 5];
_.each(arr, function(el) {
    console.log(el);
});

上面的代碼會依次輸出1, 2, 3, 4, 5,是否是頗有意思,遍歷一個數組連for循環都不用本身寫了。_.each()方法遍歷數組很是好用,可是它的內部實現一點都不難。下面就一塊兒來看看究竟是如何實現_.each()的。在這以前,咱們先來看看_.each()的API。_.each()的通常是以下調用的:數組

_.each(arr, fn, context);

它接收3個參數,瀏覽器

第一個是須要遍歷的數組( 實際上是對象也是能夠的,這個後面咱們再一塊兒來討論);
第二個是它的回調函數(這個回調函數能夠傳入3個參數,如:function(el, i, arr),分別是當前元素、當前索引和原數組);
第三個是回調函數須要綁定到的上下文,即指定回調函數fn的this值。
 
好啦,需求已經很是明確了,開始幹活啦!
咱們先來實現一個最簡單的_.each(),它不可以修改上下文this的,接收兩個參數,代碼以下:
 
var _ = {}; // 假設這就是underscore哈
 
// 一個最簡單的_.each方法的實現
_.each = function(arr, fn) {
  for(var i = 0; i < arr.length; i++) {
    fn(arr[i], i, arr);
  }
  return arr; // 把原數組返回
}

怎麼樣?是否是很簡單呢?只是用一個for循環,不停的調用回調函數便可,短短几行代碼就搞定了,相信沒有朋友看不懂的哈!下面咱們來測試一下看能不能正常工做:app

var arr = [1, 2, 3, 4, 5];
_.each(arr, function(el, i, arr) {
  console.log(el);
});

在瀏覽器打開,而後控制檯就會看到有正確的輸出了。函數

這麼簡單的代碼一點意思也沒有,接下來看一個比較有點挑戰性的例子哈。好比,數組arr有個sum屬性,咱們須要把數組全部的元素求和以後存放到sum裏面,以下:
var arr = [1, 2, 3, 4, 5];
arr.sum = 0; // sum屬性用來存放數組元素之和
_.each(arr, function(el, i, arr) {
  this.sum += el;
});

這時候,回調函數裏面用到了this,若是不綁定的話,this默認就是window,這不是咱們想要的,咱們但願它綁定到數組arr上面。call或者apply能夠實現這個功能,代碼以下:測試

var _ = {}; // 假設這就是underscore哈
 
// bind,接收兩個參數fn和context
// 把fn綁定到context上面
var bind = function(fn, context) {
  context = context || null;
  return function(el, i, arr) {
    fn.call(context, el, i, arr);
  }
}
 
// _.each
_.each = function(arr, fn, context) {
 
  // 調用bind方法,把fn綁定到context上面
  fn = bind(fn, context);
 
  for(var i = 0; i < arr.length; i++) {
    fn(arr[i], i, arr);
  }
  return arr;
}
 
 
// 測試用例:
var arr = [1, 2, 3, 4, 5];
arr.sum = 0; // sum屬性用來存放數組元素之和
 
_.each(arr, function(el, i, arr) {
  this.sum += el;
}, arr);
 
console.log(arr.sum); // 15

好啦,這個_.each()已經足夠強大了,能夠正常遍歷數組,還能夠任意指定this值改變回調函數的上下文。可是,咱們前面有提到過,Underscore的_.each()還能夠遍歷對象的 ,這個實現也不難,只要判斷一下傳入的第一個參數是對象仍是數組,若是是數組就像咱們同樣遍歷,不然若是是對象,使用對象的for...in循環遍歷就好了。有興趣的能夠本身試試,或者看看underscore的源碼。this

注意:自從ES5標準以來,原生數組就已經具備了Array.prototype.forEach、Array.prototype.Map等遍歷方法了,在項目中可使用原生的。
相關文章
相關標籤/搜索