深入理解 Array.prototype.map()


概述:

  map()方法返回一個由原數組中的每個元素調用一個指定方法后的返回值組成的新數組,它不會改變原來的數組。

  語法:

  let newArr = oldArr.map(callback[, thisArg])

參數:

  callback

    原數組中的元素調用該方法后返回一個新數組。它接收三個參數,分別為 currentValue、index、array。

    currentValue

      callback的第一個參數,數組中當前被傳遞的元素。

    index(可選)

      callback的第二個參數,數組中當前被傳遞的元素的索引。

    array(可選)

      callback的第三個參數,調用map()方法的數組,即原數組。

  thisArg(可選)

    執行callback函數時this指向的對象。

 

描述

    map()方法會給原數組中的每個元素都按順序調用一次callback函數。callback每次執行后的返回值組合起來形成一個新的數組。callback函數只會在有值的索引上被調用,那些從來沒被賦過值或者使用delete刪除的索引則不會被調用。

    callback函數會被自動傳入三個參數:數組元素、數組元素索引、原數組本身。

    如果thisArg參數有值,則每次調用callback函數時,this都會指向thisArg參數上的這個對象。如果省略了thisArg參數,或者賦值為null或undefined,則this指向全局對象。

    使用map()方法處理數組時,數組元素的范圍是在callback函數第一次被調用之前就確定了。在map()方法執行的過程中:原數組中新增加的元素將不會被callback訪問到;若已經存在的元素被改變或刪除了,則它們傳遞到callback的值是map()方法遍歷到它們那一時刻的值;而被刪除的元素將不會被訪問到。

示例

1.求數組每個元素的平方根

var numbers = [1, 4, 9];
var roots = numbers.map(Math.sqrt);
console.log(numbers) // [1, 4, 9]
console.log(roots) // [1, 2, 3]

  

代碼封裝:

var numbers = [1, 4, 9];
const arrBat = (arr, func) => arr.map(func)
var roots = arrBat(numbers, Math.sqrt)
console.log(numbers) // [1, 4, 9]
console.log(roots) // [1, 2, 3]

只需要傳入對應的處理方法,即可對數組所有元素做批處理。

當然也可對此方法進行二次封裝:

var numbers = [1, 4, 9];

const arrBat = (arr, func) => arr.map(func)
const arrToSqrt = (arr) => arrBat(arr, Math.sqrt) // 開平方根
const arrToSquare = (arr) => arrBat(arr, e => Math.pow(e, 2)) // 平方
const arrToRound = (arr) => arrBat(arr, Math.round) // 四舍五入
const arrToCeil = (arr) => arrBat(arr, Math.ceil) // 求上整
const arrToFloor = (arr) => arrBat(arr, Math.floor) // 求下整
const arrToDouble = (arr) => arrBat(arr, e => 2 * e) // 求倍數

arrToSquare(numbers) // [1, 16, 81]
arrToSqrt(numbers) // [1, 2, 3]

 

2.將數組中的單詞轉換成復數形式。

function fuzzyPlural(single){
  var result = single.replace(/o/g,"e");
  if(single === "kangaroo"){
        result += "se";
    }  
  return result;    
}

var words = ["foot","goose","moose","kangaroo"];
console.log(words.map(fuzzyPlural));  //每個元素都按順序調用一次fuzzyPlural函數 //返回["feet","geese","meese","kangareese"];

 

3.使用map重新格式化數組中的對象

var kvArray = [{key: 1, value: 10}, {key: 2, value: 20}, {key: 3, value: 30}]

var reformattedArray = kvArray.map(function(obj) { 
   var rObj = {};
   rObj[obj.key] = obj.value;
   return rObj;
});
// reformattedArry數組為: [{1: 10}, {2: 20}, {3: 30}]

// kvArray 數組未被修改: 
// [{key: 1, value: 10}, 
//  {key: 2, value: 20}, 
//  {key: 3, value: 30}]

 

代碼封裝: 

var kvArray = [{key: 1, value: 10}, {key: 2, value: 20}, {key: 3, value: 30}]

kvArrayToObjArray = (obj) => obj.map(e => {
  var rArr = [];
  rArr.push(e.key, e.value);
  return rArr;
})

var reformattedArray = kvArrayToObjArray(kvArray)
// [[1, 10], [2, 20], [3, 30]]

 

 

 

4.反轉字符串

var str = 'Hello';
Array.prototype.map.call(str, function(x) {
  return x;
}).reverse().join(''); // 'olleH'

 

代碼封裝①: 

 

const reverseStr = str => Array.prototype.map.call(str, e => e).reverse().join('')
c = reverseStr('Hello') // 'olleH'

 

代碼封裝②:使用ES6解析

 

const reverseString = str => [...str].reverse().join('');

console.log(reverseString('foobar')) // 'raboof'

 

 

5.將字符串轉為ASIIC碼組成的數組

 

var map = Array.prototype.map;
var a = map.call("hello world",function(x){ return x.charCodeAt(0);})

//a的值為[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]

 

 

代碼封裝:

const strToAscii = str => Array.prototype.map.call(str, e => e.charCodeAt(0))
console.log(strToAscii("Hello World")) // [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]

 

6.Dom操作

遍歷用 querySelectorAll 得到的動態對象集合。在這里,我們獲得了文檔里所有選中的選項,並將其打印

 

var elems = document.querySelectorAll('select option:checked');
var values = Array.prototype.map.call(elems, function(obj) {
  return obj.value;
});

 

7. 多參數函數批量轉化的誤區

 相比看完上面的解說覺得自己已經深入理解了吧,那么在看下面一個例子

["1", "2", "3"].map(parseInt);

第一反應,這里應該返回的是 [1, 2, 3],然而,實際上返回的卻是 [1, NaN, NaN]。

這是為什么呢?

   通常情況下,map 方法中的 callback 函數只需要接受一個參數,就是正在被遍歷的數組元素本身。但這並不意味着 map 只給 callback 傳了一個參數。這個思維慣性可能會讓我們犯一個很容易犯的錯誤。

 

// 下面的語句返回什么呢:
["1", "2", "3"].map(parseInt);
// 你可能覺的會是[1, 2, 3]
// 但實際的結果是 [1, NaN, NaN]

// 通常使用parseInt時,只需要傳遞一個參數.
// 但實際上,parseInt可以有兩個參數.第二個參數是進制數.
// 可以通過語句"alert(parseInt.length)===2"來驗證.
// map方法在調用callback函數時,會給它傳遞三個參數:當前正在遍歷的元素, 
// 元素索引, 原數組本身.
// 第三個參數parseInt會忽視, 但第二個參數不會,也就是說,
// parseInt把傳過來的索引值當成進制數來使用.從而返回了NaN.

function returnInt(element) {
  return parseInt(element, 10);
}

['1', '2', '3'].map(returnInt); // [1, 2, 3]
// 意料之中的結果

// 也可以使用簡單的箭頭函數,結果同上
['1', '2', '3'].map( str => parseInt(str) );

// 一個更簡單的方式:
['1', '2', '3'].map(Number); // [1, 2, 3]
// 與`parseInt` 不同,下面的結果會返回浮點數或指數:
['1.1', '2.2e2', '3e300'].map(Number); // [1.1, 220, 3e+300]

 

8.封裝Array.prototype.map()方法

下面是實現map方法的函數封裝。 暫時只是實現了基礎功能,具體還有很多優化可以做。

   function myMap(data, fn) {
        var arg = [];
        for (var i = 0; i < data.length; i++) {
            (function (ele, fn) {
            //每一個元素處理后放入新數組
                arg.push(fn(ele));
            })(data[i], fn);
        }
        return arg;
    }
    var data = [10, 20, 30, 40];
    var roots= myMap(data, function (ele) {
        return ele / 10;
    });
    console.log(roots);//[1,2,3,4]

 

兼容舊環境 

map 是在最近的 ECMA-262 標准中新添加的方法;所以一些舊版本的瀏覽器可能沒有實現該方法。在那些沒有原生支持 map 方法的瀏覽器中,你可以使用下面的 Javascript 代碼來實現它。所使用的算法正是 ECMA-262,第 5 版規定的。假定ObjectTypeError, 和 Array 有他們的原始值。而且 callback.call 的原始值也是 Function.prototype.call

 

 1 // 實現 ECMA-262, Edition 5, 15.4.4.19
 2 // 參考: http://es5.github.com/#x15.4.4.19
 3 if (!Array.prototype.map) {
 4   Array.prototype.map = function(callback, thisArg) {
 5 
 6     var T, A, k;
 7 
 8     if (this == null) {
 9       throw new TypeError(" this is null or not defined");
10     }
11 
12     // 1. 將O賦值為調用map方法的數組.
13     var O = Object(this);
14 
15     // 2.將len賦值為數組O的長度.
16     var len = O.length >>> 0;
17 
18     // 3.如果callback不是函數,則拋出TypeError異常.
19     if (Object.prototype.toString.call(callback) != "[object Function]") {
20       throw new TypeError(callback + " is not a function");
21     }
22 
23     // 4. 如果參數thisArg有值,則將T賦值為thisArg;否則T為undefined.
24     if (thisArg) {
25       T = thisArg;
26     }
27 
28     // 5. 創建新數組A,長度為原數組O長度len
29     A = new Array(len);
30 
31     // 6. 將k賦值為0
32     k = 0;
33 
34     // 7. 當 k < len 時,執行循環.
35     while(k < len) {
36 
37       var kValue, mappedValue;
38 
39       //遍歷O,k為原數組索引
40       if (k in O) {
41 
42         //kValue為索引k對應的值.
43         kValue = O[ k ];
44 
45         // 執行callback,this指向T,參數有三個.分別是kValue:值,k:索引,O:原數組.
46         mappedValue = callback.call(T, kValue, k, O);
47 
48         // 返回值添加到新數組A中.
49         A[ k ] = mappedValue;
50       }
51       // k自增1
52       k++;
53     }
54 
55     // 8. 返回新數組A
56     return A;
57   };      
58 }

 

 

 

 

參考資料:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/map

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM