JavaScript的迭代函式與迭代函式的實現

小綠和小藍發表於2019-01-07

Image result for JavaScript 迭代

前言

如果對技比較自信,請直接看實現的原始碼

如果想回顧一下基礎,請按文章順序閱讀


說到迭代方法,最先想到的是什麼?forEach還是map,迭代的方法ES5提供了5種方法

以下定義來自 JavaScript高階程式設計

每個方法都接收兩個引數

  1. 在每一項上執行的函式
  2. 執行該函式的作用域物件(影響this的值)

傳入這些方法中的函式會接收3個引數

  1. 陣列項的值
  2. 該項在陣列的位置
  3. 陣列物件本身

迭代函式執行後可能會可能不會影響返回結果 (霧..)

ES5提供的迭代函式

  • forEach(): 對陣列中的每一項執行給定函式,無返回值
  • every(): 對陣列中的每一項執行給定函式,如果該函式每一項都返回true,則返回true
  • some(): 對陣列中的每一項執行給定函式,如果該函式任意一項返回true,則返回true
  • map(): 對陣列中的每一項執行給定函式,返回每次函式呼叫的結果組成的陣列
  • filter(): 對陣列中的每一項執行給定函式,該函式會返回true的項組成的陣列

引數說明

let array = [1,2,3,4,5,6,7,8,9]
array.forEach((element,index,array) => {
  console.log(`當前遍歷元素${element}`);
  console.log(`當前元素位置${index}`);
  console.log(`陣列本身${array}`);
})
> 當前遍歷元素1
> 當前元素位置0
> 陣列本身1,2,3,4,5,6,7,8,9
> 當前遍歷元素2
> 當前元素位置1
> 陣列本身1,2,3,4,5,6,7,8,9
> 當前遍歷元素3
> 當前元素位置2
> 陣列本身1,2,3,4,5,6,7,8,9
複製程式碼

forEach可以說是最常用的一個迭代方法了,該方法沒有返回值,與for迴圈的效果一樣

forEach的第二個引數,js高程上說明 是執行該函式的作用域物件,可以看一下經典的例子

let obj2 = {
  name: '張三',
  times:[1,2,3],
  print:function () {
    this.times.forEach(function(res) {
      console.log(this.name);
    },this)
  }
}
// 迭代函式內部的function預設指向windows 第二個引數調整了this指向
obj2.print()
// 張三
// 張三
// 張三
複製程式碼

如果這麼寫看不太懂的話,看箭頭函式的寫法一下子就能明白

let obj2 = {
  name: '張三',
  times:[1,2,3],
  print:function () {
    this.times.forEach(res => {
      console.log(name);
    })
  }
}
// 箭頭函式this指向父級,所以他不需要調整this
obj2.print()
// 張三
// 張三
// 張三
複製程式碼

every(判斷函式)

對陣列中的每一項執行給定函式,如果該函式每一項都返回true,則返回true

預設返回false

var array = [1,2,3,4,5,6,7,8,9]
var result = array.every(e => {})
console.log(result); // 
> false 
複製程式碼

全部ture才會返回true

var array = [1,2,3,4,5,6,7,8,9]
var result = array.every(e => {
  return e > 0
})
console.log(result);
> true

var array = [1,2,3,4,5,6,7,8,9]
var result = array.every(e => {
  return e > 1
})
console.log(result);
> false
複製程式碼

some(判斷函式)

對陣列中的每一項執行給定函式,如果該函式任意一項返回true,則返回true

預設返回false

var array = [1,2,3,4,5,6,7,8,9]
var result = array.some(e => {})
console.log(result); // 
> false 
複製程式碼

全部false才會返回false

var array = [1,2,3,4,5,6,7,8,9]
var result = array.some(e => {
  return e > 8
})
console.log(result);

var array = [1,2,3,4,5,6,7,8,9]
var result = array.some(e => {
  return e > 9
})
console.log(result);
> false
複製程式碼

以上兩個都不是很常用,但是毫無疑問在特定的需求下,這個要比用forEach程式碼要簡潔很多

filter(過濾函式)

對陣列中的每一項執行給定函式,該函式會返回true的項組成的陣列

var array = [1,2,3,4,5,6,7,8,9]
var result = array.filter(e => {
  return e>5
})
console.log(result);
> [6, 7, 8, 9]
複製程式碼

上面的例子,array陣列裡面大於5的數會被過濾出來,filter函式在日常當中比較常用

map(處理函式)

對陣列中的每一項執行給定函式,返回每次函式呼叫的結果組成的陣列

var array = [1,2,3,4,5,6,7,8,9]
var result = array.map(e => {
  return e>5
})
console.log(result);
> [false, false, false, false, false, true, true, true, true]

var array = [1,2,3,4,5,6,7,8,9]
var result = array.map(e => {
  return e*2
})
console.log(result);
> [2, 4, 6, 8, 10, 12, 14, 16, 18]
複製程式碼

forEach(迭代函式)

對陣列中的每一項執行給定函式,無返回值

var array = [1,2,3,4,5,6,7,8,9]
var arraypush = []
var result = array.forEach(e => {
  if (e > 5) {
    arraypush.push(e)
  }
})
console.log(arraypush);
> [6, 7, 8, 9]
複製程式碼

最純粹的迭代函式,似乎forEach是處理外部陣列最好的選擇

到這裡,我想起了我第一次使用filter函式的時候,我驚呆了,這函式太強大了!

如此好用的工具函式,不自己實現一遍怎麼能做到完全瞭解

以下函式為自己實現的,並不是原始碼,若有錯誤請指點!

實現forEach

首先明顯forEach是Array上的原型鏈上的函式所以第一件事就是建立一個原型方法

Array.prototype.MyforEach = function (){}
複製程式碼

forEact 第一個引數為一個匿名函式 第二個引數為this指向 所以

Array.prototype.MyforEach = function (fn,obj){}
複製程式碼

forEach會迭代呼叫它的陣列所以內部肯定是迴圈

Array.prototype.MyforEach = function (fn,obj){
  let len = this.length
  for (let index = 0; index < len; index++) {
    fn(this[index],index,this)
  }
}
複製程式碼

但是我們還沒有考慮this指向的事情,所以還需要新增一些調整this的程式碼

Array.prototype.MyforEach = function (fn,obj){
  let len = this.length
  if (obj != undefined) {
    fn = fn.bind(obj)
  }
  for (let index = 0; index < len; index++) {
    fn(this[index],index,this)
  }
}
複製程式碼

執行一下試試,就用之前的例子

var array = [1,2,3,4,5,6,7,8,9]
Array.prototype.MyforEach = function (fn,obj){
  let len = this.length
  if (obj != undefined) {
    fn = fn.bind(obj)
  }
  for (let index = 0; index < len; index++) {
    fn(this[index],index,this)
  }
}

var obj2 = {
  name: '張三',
  times:[1,2,3],
  print:function () {
    this.times.MyforEach(function(res) {
      console.log(this.name);
    },this)
  }
}
obj2.print()
> 張三
> 張三
> 張三
複製程式碼

實現map

map與forEach的區別是

  1. map中如果是運算,會返回每次函式呼叫的新的結果組成的陣列
  2. map中如果是判斷,會返回每次迭代結果組成的陣列

所以只要在迭代函式內部建立一個陣列,每次迭代都push進去,最後返回出去就好啦

Array.prototype.Mymap = function (fn,obj){
  var resultData = []
  var len = this.length
  if (obj != undefined) {
    fn = fn.bind(obj)
  }
  for (let index = 0; index < len; index++) {
    resultData.push(fn(this[index],index,this))
  }
  return resultData
}
複製程式碼

執行一下

var array = [1,2,3,4,5,6,7,8,9,]
var result = array.Mymap(e => {
    return e*2
})
console.log(result);
> [2, 4, 6, 8, 10, 12, 14, 16, 18]
複製程式碼

實現 some every

some與every都會有一個特點 預設返回false

不同的地方在於

some要求 全部返回false返回false

every要求 全部返回true返回true

// -- every -- 
Array.prototype.Myevery = function (fn,obj) {
  var len = this.length
  if (obj != undefined) {
    fn = fn.bind(obj)
  }
  for (let index = 0; index < len; index++) {
    if (fn(this[index],index,this) == undefined) { // 無返回值 預設返回false
      return false
    }else if (fn(this[index],index,this) !== true) { // 出現一個不為 true 就停止迭代 返回結果
      return false
    }
  }
  return true
}

// -- some -- 
Array.prototype.Mysome = function (fn,obj) {
  var len = this.length
  if (obj != undefined) {
    fn = fn.bind(obj)
  }
  for (let index = 0; index < len; index++) {
    if (fn(this[index],index,this) == undefined) {
      return false
    } else if (fn(this[index],index,this) !== false) {
      return true
    }
  }
  return false
}
複製程式碼

實現fliter

相信到這裡,你也可以直接實現一個fliter函式了,僅僅是多新增一個陣列

Array.prototype.Myfilter = function (fn, obj) {
  let resultData = []
  var len = this.length
  if (obj != undefined) {
    fn = fn.bind(obj)
  }
  for (let index = 0; index < len; index++) {
    if (fn(this[index],index,this)) { // 返回true則進行處理
      resultData.push(this[index]) // 注意不是push函式結果
    }
  }
  return resultData
}

// -- 執行 -- 

var array = [1,2,3,4,5,6,7,8,9]
var result = array.Myfilter(e => {
  return e>5
})
console.log(result);
>  [6, 7, 8, 9]
複製程式碼

perfect! ?

原來很多東西,並沒有想象的那麼複雜

後記

想起了之前掘金上的 停止學習框架 一文,以及後面各位大佬的 駁 ....停止學習框架,說到底都是為了告訴我們,不管學習什麼,都要打好基礎,作為前端開發者,最最基礎的就是打好JavaScript的基礎,基礎紮實,學習框架都不是困難事情

相關文章