深刻理解 Array.prototype.map()

概述:

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

  語法:github

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

參數:

  callback數組

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

    currentValueapp

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

    index(可選)優化

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

    array(可選)es5

      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

相關文章
相關標籤/搜索