一起來實現es5中新增的數組方法

前言

一直以來想寫一篇關于es5中新增數組的使用方法與源碼實現的文章,拖了挺久了,趁著這夜深人靜,大腦清醒,又困意不在的時刻寫下來。也許有人會問,現如今es6都大行其道了,還學es5是不是有些過時了,??,溫故而知新,可以為師矣,而且我們是要自己實現這些方法嘛,知其然還要知其所以然,不光要會用,還要知道為什么是這樣用噠。

前端配圖

新增方法預覽

es5中給js的數組增添了許多實用的方法,利用這些方法可以幫助我們更加快速方便的寫js代碼,然后蛋疼的是低版本ie肯定是不支持的,所以..................自己動手豐衣足食。讓我們一步步看下如何使用與實現這些方法。

  1. forEach
  2. map
  3. filter
  4. some
  5. every
  6. indexOf
  7. lastIndexOf
  8. reduce
  9. reduceRight

forEach

這個方法作用是啥咧,就是循環,遍歷。比如一般我們在for循環做這樣的事的時候如下。


var arr = [1, 2, 3, 4, 5, 6];

for (var i = 0, len = arr.length; i < len; i++) {
  console.log(arr[i], i, arr);
}


如果用forEach我們應該如何做呢?

var arr = [1, 2, 3, 4, 5, 6];

arr.forEach(function (e, i, array) {
  console.log(e, i, array)
})

是不是覺得不用寫for循環了,瞬間逼格都高了

forEach函數中的回調函數支持三個參數,1、數組的值2、值的索引3、數組本身。這樣的調用方式是不是和jQuery中的$.each很像? 其實不然,jQuery和forEach回調函數的第一個和第二個參數正好是反著來的。

看看對比

var arr = [1, 2, 3, 4, 5];

// forEach
arr.forEach(function (e, i, array) {
  console.log(e, i, array);
})

// output

1 0 [1, 2, 3, 4, 5]
2 1 [1, 2, 3, 4, 5]
3 2 [1, 2, 3, 4, 5]
4 3 [1, 2, 3, 4, 5]
5 4 [1, 2, 3, 4, 5]


// $.each
$.each(arr, function (i, e, array) { // 測試的時候發現array是undefined,查了文檔也發現沒有第三個參數
  console.log(i, e, array);
})

// output

0 1 undefined
1 2 undefined
2 3 undefined
3 4 undefined
4 5 undefined

接著我們來看一下forEach的第二個參數,這個參數決定第一個回調函數的內部this指向


var arr = [1, 2, 3, 4, 5];

//  默認情況下,第二個參數不傳入時
arr.forEach(function (e, i, array) {
  console.log(e, i, array, this);
})

// output
1 0 [1, 2, 3, 4, 5] window 
2 1 [1, 2, 3, 4, 5] window 
3 2 [1, 2, 3, 4, 5] window 
4 3 [1, 2, 3, 4, 5] window 
5 4 [1, 2, 3, 4, 5] window 

// 傳入參數
arr.forEach(function (e, i, array) {
  console.log(e, i, array, this);
}, {name: 'qianlong'})

// output
1 0 [1, 2, 3, 4, 5] {name: 'qianlong'} 
2 1 [1, 2, 3, 4, 5] {name: 'qianlong'} 
3 2 [1, 2, 3, 4, 5] {name: 'qianlong'} 
4 3 [1, 2, 3, 4, 5] {name: 'qianlong'} 
5 4 [1, 2, 3, 4, 5] {name: 'qianlong'} 

最后接下來我們自己實現一下這個方法

var ObjPro = Object.prototype,
    hasOwn = ObjPro.hasOwnProperty,
    nativeArray = ObjPro.forEach;

Array.prototype.forEach = nativeArray || function (callBack, ctx) {  
  if (typeof callBack != 'function') return;
  
  for (var i =0, len = this.length; i < len; i++) {
    if (hasOwn.call(this, i)) {
      callBack.call(ctx, this[i], i, this);
    }
  }
}

map

map是干嘛的! 其最主要的作用就是將原數組按照一定的規則映射成一個新的數組。再將其返回,注意是返回一個新的數組,而不是將原數組直接改變使用方式和forEach類似,也是接受一個回調函數,一個改變內部this指向的對象。

map

array.map(callback,[ thisObject])

callback

var arr = [1, 2, 3, 4, 5];

arr.map(function(value, index, array) {
  
});

舉個栗子

var arr = [1, 2, 3, 4, 5];

var newArr = arr.map(function (e, i, array) {
  return 'hello ' +  e;
})

// output

["hello 1", "hello 2", "hello 3", "hello 4", "hello 5"] // newArr

[1, 2, 3, 4, 5] // arr

注意上面的return,如果我們不寫return會怎樣呢?

var arr = [1, 2, 3, 4, 5];

var newArr = arr.map(function (e, i, array) {
  'hello ' +  e;
})

// output

[undefined, undefined, undefined, undefined, undefined] // newArr

[1, 2, 3, 4, 5] // arr

這一堆的undefined是啥情況,還記得一個函數執行完,如果沒有顯示的返回值,會返回什么嗎? 沒錯 就是undefined,這就是原因所在,等會通過源碼,你就會更加明白。

最后我們自己實現一下map這個方法

var ObjPro = Object.prototype,
    hasOwn = ObjPro.hasOwnProperty,
    nativeMap = ObjPro.map;
    
Array.prototype.map = nativeMap || function (callBack, ctx) {
  if (typeof callBack != 'function') return;
  
  var returnArr = [];
  for(var i = 0, len = this.length; i < len; i++) {
    returnArr.push(callBack.call(ctx, this[i], i, this)); // 這就是為什么回調函數沒有返回值的情況下會得到一堆的undefined值,他將回調函數的返回值push到了一個數組里面,當你沒有顯示的返回值的時候,自然push進去的就是undefined了     
  }
  return  returnArr;
}    

filter

接下來是filter,篩選,過濾的意思,給你一個數組,用一些你制定的條件,對其中的值進行過濾,最后得到你想要的新的數組。基本用法和map差不多

  
array.filter(callback,[ thisObject]);

但是和map也有差別的地方,filter需要你在callback處返回弱等于true 的值,才會將原數組中篩選出的值返回給你。

舉個栗子


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

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

// output

[1, 2, 3, 4, 5] // newArr

var newArr2 = arr.filter(function (e, i, array) {
  if (e >= 2) return true; 
})

// ouput
[2, 3, 4, 5] // newArr2

當然最后還有第二個參數改變內部this指向的參數可選,默認是window對象,你也可以傳一個對象進去, 最后我們自己來實現一下這個api

var ObjPro = Object.prototype,
    hasOwn = ObjPro.hasOwnProperty,
    nativeFilter = ObjPro.filter;
    
Array.prototype.filter = nativeFilter || function (callBack, ctx) {
  if (typeof callBack != 'function') return;
  
  var returnArr = [];
  for(var i = 0, len = this.length; i < len; i++) {
    if (callBack.call(ctx, this[i], i, this)) {
      returnArr.push(this[i]);
    }
  } 
  return returnArr;
}


some vs every

some與接下里的every正好相對,some是只要數組中的某個值,符合你給定的判斷條件就返回true,而every則是數組中的所有值都符合你給定的判斷條件的時候才會返回true,否則就返回false,也就是說兩個方法最后得到的都是true or false

舉個栗子

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

var result = arr.some(function (e, i, array) {
  if (e === 3) {return true};
});

// output   
true // result;

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

var result2 = arr.every(function (e, i, array) {
  if (e > 3) {return true};
});
// output   
false // result;

some 和 every使用起來非常簡單,接下來我們自己實現一把


var ObjPro = Object.prototype,
    hasOwn = ObjPro.hasOwnProperty,
    nativeSome = ObjPro.some,
    nativeEvery = ObjPro.every;
    
// some    
Array.prototype.some = nativeSome || function (callBack, ctx) {
  if (typeof callBack != 'function') return;
  
  var resultValue = false;
  for(var i = 0, len = this.length; i < len; i++) {
    if (resultValue) {
      break;
    }
    resultValue = !!callBack.call(ctx, this[i], i, this); 
  }
  return  resultValue;
} 

// every

Array.prototype.every = nativeEvery || function (callBack, ctx) {
  if (typeof callBack != 'function') return;

  var resultValue = true;
  for (var i = 0, len = this.length; i < len; i++) {
    if (!resultValue) {
      break;    
    }
    resultValue = !!callBack.call(ctx, this[i], i, this);
  } 
  return resultValue;   
}   

indexOf

數組的indexOf方法和字符串的indexOf用法非常類似,array.indexOf(searchElement[, fromIndex]),針對給定的要查找的值,和開始查找的位置(可選),返回整數索引值。

舉個例子


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

arr.indexOf(1) // 1
arr.indexOf(3, 'qianlong') // 3 因為給定的開始索引值不能轉化成數字,所以還是從0位置開始搜索 
arr.indexOf(3, 4) // -1
arr.indexOf(3, '4') // -1
arr.indexOf('3') // -1 // 判斷條件是強 3 !== '3' => -1

實現代碼


var ObjPro = Object.prototype,
    hasOwn = ObjPro.hasOwnProperty,
    nativeIndexOf = ObjPro.indexOf;
    
Array.prototype.indexOf = nativeIndexOf || function (searchElement, fromIndex) {
  var returnIndex = -1,
      fromIndex = fromIndex * 1 || 0;
  for (var i = fromIndex, len = this.length; i < len; i++) {
    if (searchElement === this[i]) {
       returnIndex = i;
       break;
    }
  }             
  return returnIndex;       
}


lastIndexOf

數組的lastIndexOf方法和字符串的lastIndexOf用法非常類似,array. lastIndexOf(searchElement[, fromIndex]),針對給定的要查找的值,和開始查找的位置(可選),返回整數索引值。與indexOf不同的地方在于,它是從后往前查找。默認開始查找的位置是 array.length - 1

舉個栗子

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

arr.lastIndexOf(1) // 9
arr.lastIndexOf(3, 'qianlong') // -1 這里和indexOf不一樣,傳入的值不能轉化為數字將得到-1
arr.lastIndexOf(3, 4) // 3
arr.lastIndexOf(3, '4') // 3
arr.lastIndexOf('3') // -1 // 判斷條件是強 3 !== '3' => -1

源碼實現

var ObjPro = Object.prototype,
    hasOwn = ObjPro.hasOwnProperty,
    nativeLastIndexOf = ObjPro.lastIndexOf;
    
Array.prototype.lastIndexOf = nativeLastIndexOf || function (searchElement, fromIndex) {
  var len = this.length,
      returnIndex = -1,
      fromIndex = fromIndex * 1 || len - 1;
      
  for (var i = fromIndex; i > -1; i -= 1) {
    if (this[i] === searchElement){
      returnIndex = i;
      break;
    }
  }
  return returnIndex;
}

reduce

reduce 相對es5中數添加的其他方法都復雜一些,我們可以通過栗子來看一下這個api怎么使用。首先基本參數如下

array.reduce(callback[, initialValue]),接收一個回調函數,一個初始化的值initialValue。其中callback參數分別是初始化的值initialValue,如果沒有傳入initialValue,則默認是數組的第一項。第二個及其后面的參數分別是當前值,索引,數組本身

var arr = [0, 1, 2, 3, 4, 5],
    sum = arr.reduce(function (init, cur, i, array) {
      return init + cur;
    }); 
    
   //output
   sum // 15
   

我們來看一下上面的執行過程是怎樣的。

第一回合

// 因為initialValue沒有傳入所以回調函數的第一個參數為數組的第一項

init = 0;
cur = 1;

=> init + cur = 1;

第二回合

init = 1;
cur = 2;

=> init + cur = 3;

第三回合

init = 3;
cur = 3;

=> init + cur = 6;

第四回合

init = 6;
cur = 4;

=> init + cur = 10;

第五回合

init = 10;
cur = 5;

=> init + cur = 15;

最后得到結果15

那么我們如何自己實現一個reduce呢?

var ObjPro = Object.prototype,
    hasOwn = ObjPro.hasOwnProperty,
    nativeReduce = ObjPro.reduce;
    
Array.prototype.reduce = nativeReduce || function (callBack, initialVal) {
  if (typeof callBack != 'function') return;
  
  var init = initialVal,
      i = 0;
  
      if (init === void (0)) {
        init = this[0];
        i = 1;
      }
  for (i, len = this.length; i < len; i++) {
    if (hasOwn.call(this, i)) {
      init = callBack(init, this[i], i, this);
    }  
  }
  return init;    
}   
    

reduceRight

reduceRight基本用法與reduce類似,好比indexOf與lastIndexOf,不同之處在于它是從最右邊的值開始計算的。我們直接去看源碼怎么實現吧

var ObjPro = Object.prototype,
    hasOwn = ObjPro.hasOwnProperty,
    nativeReduceRight = ObjPro.reduceRight;
   
Array.prototype.reduceRight = nativeReduceRight || function (callBack, initialVal) {
  if (typeof callBack != 'function') return;
  
  var init = initialVal,
      len = this.length,
      i = len - 1;
      
  if (init === void(0)) {
    init = this[len - 1];
    i -= 1;
  }
  for (i; i > -1; i -=1) {
    if (hasOwn.call(this, i)) {
      init = callBack(init, this[i], i, this);
    }
  }    
  return init;
}     

結尾

終于寫完了,斷斷續續快寫了兩天,歡迎大家看了以后提一些意見,函數實現的不一定都對,肯定有一些問題的地方,歡迎大家指正。

最后把代碼放到github上面了

github地址

各位大大,請讓我打個小廣告。??????

博客地址

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,923評論 6 535
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,740評論 3 420
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,856評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,175評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,931評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,321評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,383評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,533評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,082評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,891評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,618評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,319評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,732評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,987評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,794評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,076評論 2 375

推薦閱讀更多精彩內容