對Array.prototype.map的一些認識

嘿_該學習了發表於2019-04-17

前言

寫部落格真是一項技術活,經常逛掘金看大佬寫的文章。我覺得我也應該嘗試的寫點東西,加深自己對它的認識,也方便以後查閱。

map方法的基本用法

MDN上對map方法的介紹是:map() 方法建立一個新陣列,其結果是該陣列中的每個元素都呼叫一個提供的函式後返回的結果。

從官方的介紹中我們可以瞭解到,map方法不會改變呼叫它的陣列(但是我們可以在提供的回撥函式中修改),而是返回一個新的陣列。

舉個例子:

  • 對陣列中的元素進行處理
let arr = [1, 2, 3]
let mappedArr = arr.map(value => {
  return value + 1
})
console.log(mappedArr) // [2, 3, 4]
複製程式碼

原理剖析

通過上面的例子,我們大概知道了map函式工作基本工作原理了。什麼?就這能知道工作原理了?

額,那我們進一步剖析。

let mappedArr = arr.map(function callback(currentValue[, index[, array]]) {
 // Return element for new_array 
}[, thisArg])
複製程式碼

map方法可以接受兩個引數:第一個引數callback是一個函式,這個引數是必需的。callback中有三個可選引數,分別是當前的正在處理的陣列元素、當前處理元素的索引、呼叫map方法的陣列;第二個引數是可選的,它的作用是用來定義執行callback時的this指向。

知道了各個引數的作用之後,就可以搞事情了。

實現基本版的自定義map

Array.prototype._map = function (callback, thisArg) {
  let len = this.length
  let result = []
  for (let i = 0; i < len; i++) {
    // 通過呼叫call方法,將callback的this指向thisArg,
    // 後面依次傳入當前處理的元素、當前元素下標、呼叫map方法的陣列
    // 當thisArg為undefined或者null時,this就指向全域性的window了.這正好與原生的map方法一致.
    let temp = callback.call(thisArg, arr[i], i, arr)
    result[i] = temp
  }
  return result
}
let arr = [1, 2, 3]
let result = arr._map(function (value, index, arr) {
  return value + 1
})
console.log(result) // [2, 3, 4]
複製程式碼

這樣就實現了一個陣列的map方法,但是這樣實現的與原生的有很多區別:

原生的map方法會跳過被用delete刪除或者未定義的元素
// 注意: 這裡的arr[3]就是一個未定義的元素
let arr = [1, 2, 3, , undefined]
let result = arr._map(function (value, index, arr) {
  return value + '1'
})

複製程式碼
  • 原生的結果
console.log(arr) // [1, 2, 3, empty, undefined]
console.log(result) // ["11", "21", "31", empty, "undefined1"]
複製程式碼
  • 上面自定義map方法的結果
console.log(arr) // [1, 2, 3, empty, undefined]
console.log(result) // ["11", "21", "31", "undefined1", "undefined1"]
複製程式碼

這裡未定義的元素我覺得應該是在陣列中通過索引獲取不到值的元素,也就是這個元素根本不存在。我上面自定義的map方法其實是沒有注意到這一點的,當獲取一個陣列中不存在的元素,肯定返回的是undefined,然後就得到了上面的結果。

那我們該怎麼去解決這一問題呢?

in運算子

in運算子是用來檢測一個物件是否含有特定的屬性。但是說到in就不得不提一下Object.prototype.hasOwnProperty()這個方法了,雖然二者都可以用來檢測某個物件上是否有特定的屬性,但是後者會忽略掉從原型上繼承過來的屬性。

  • 舉個例子
    • 使用in運算子
      對Array.prototype.map的一些認識
      在JavaScript中,陣列也是物件,陣列的索引就可以看做是屬性了。從圖中可以看到,陣列arr自身是沒有push方法的,但是通過原型鏈可以找到arr.__proto__.push,所以呢,圖中最後一個表示式的只為true
    • 使用Object.prototype.hasOwnProperty()方法

對Array.prototype.map的一些認識
hasOwnProperty這個方法侷限在物件自身的屬性上,它會忽略掉從原型鏈上繼承過來的屬性。

完善功能
  1. 對未定義的元素,讓回撥不被其呼叫;
  2. 對於傳入的callback不是函式給出異常提示。

程式碼如下:

Array.prototype._map = function (callback, thisArg) {
  let result
  // callback必須是一個函式,否則丟擲異常
  if (Object.prototype.toString.call(callback) !== '[object Function]') {
    throw new Error(callback + 'is not a function')
  }
  let len = this.length
  // 建立結果陣列,長度和原陣列相同
  result = new Array(len)
  for (let i in this) {
    let currentVal, mappedVal
    currentVal = this[i]
    mappedVal = callback.call(thisArg, currentVal, i, this)
    result[i] = mappedVal
  }
  return result
}
複製程式碼
  • 測試功能
let arr = [1, 2, 3, , undefined]
let result = arr._map(function (value) {
  return value + '1'
})
console.log(arr) // [1, 2, 3, empty, undefined]
console.log(result) // ["11", "21", "31", empty, "undefined1"]
複製程式碼

寫在最後

寫這篇文章的目的,也是在於鍛鍊自己的動手能力和語言表達能力。希望無意看到這篇文章的大佬指點指點,給出你們寶貴的建議,謝謝。

相關文章