今天在地鐵上看到這樣一個小例子:
["1","2","3"].map(parseInt);
相信很多人和我一樣,覺得輸出的結果是[1,2,3]。no!no!!no!!!正確的答案是[1,NaN,NaN]。當時我百思不得其解,於是到了公司之后就開始查閱資料,終於弄明白了。
我們先來介紹一下map()方法:
概述:
map()方法返回一個由原數組中的每個元素調用一個指定方法后的返回值組成的新數組,它不會改變原來的數組。
語法:
array.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()方法遍歷到它們那一時刻的值;而被刪除的元素將不會被訪問到。
總結
通常情況下,map()方法中的callback函數只接受一個參數,就是正在被遍歷的數組元素本身。但這並不意味着map只給callback傳了一個參數。這個思維慣性可能會讓我們犯一個很容易犯的錯誤。
兼容舊環境
map()方法是在最近的 ECMA-262 標准中新添加的方法;所以一些舊版本的瀏覽器可能沒有實現該方法。在那些沒有原生支持 map() 方法的瀏覽器中,你可以使用下面的 Javascript 代碼來實現它。所使用的算法正是 ECMA-262,第 5 版規定的。假定Object、TypeError和 Array 有他們的原始值。而且callback.call的原始值也是 Function.prototype.call
// 實現 ECMA-262, Edition 5, 15.4.4.19 // 參考: http://es5.github.com/#x15.4.4.19 if (!Array.prototype.map) { Array.prototype.map = function(callback, thisArg) { var T, A, k; if (this == null) { throw new TypeError(" this is null or not defined"); } // 1. 將O賦值為調用map方法的數組. var O = Object(this); // 2.將len賦值為數組O的長度. var len = O.length >>> 0; // 3.如果callback不是函數,則拋出TypeError異常. if (Object.prototype.toString.call(callback) != "[object Function]") { throw new TypeError(callback + " is not a function"); } // 4. 如果參數thisArg有值,則將T賦值為thisArg;否則T為undefined. if (thisArg) { T = thisArg; } // 5. 創建新數組A,長度為原數組O長度len A = new Array(len); // 6. 將k賦值為0 k = 0; // 7. 當 k < len 時,執行循環. while(k < len) { var kValue, mappedValue; //遍歷O,k為原數組索引 if (k in O) { //kValue為索引k對應的值. kValue = O[ k ]; // 執行callback,this指向T,參數有三個.分別是kValue:值,k:索引,O:原數組. mappedValue = callback.call(T, kValue, k, O); // 返回值添加到新數組A中. A[ k ] = mappedValue; } // k自增1 k++; } // 8. 返回新數組A return A; }; }
了解了map()方法,現在我們回過頭來看看文章開頭提到的那個小例子。
通常使用parseInt時,只需要傳遞一個參數。但實際上,parseInt可以有兩個參數,第二個參數是進制數,可以通過語句“alert(parseInt.length) === 2”來驗證。
map()方法在調用callback函數時,會給它傳遞三個參數:當前正在遍歷的元素、元素索引、原數組本身。第三個參數parseInt會忽視,但第二個參數不會,也就是說,parseInt把傳過來的索引值當成進制數來使用,而parseInt的第二個參數的范圍為2~36(不包含2但包含36),如果省略該參數或者其值為0,則數字將以10為基數來解析;如果小於2或者大於36,parseInt()將返回NaN,所以最終返回了[1,NaN,NaN]。
實際上,這個小例子可以分解成這樣理解:
parseInt("1",0); //基數為0,以十進制來解析,返回1
parseInt("2",1) //基數為1,小於2,返回NaN
parseInt("3",2) //基數為2,小於2,返回NaN
注意:在測試parseInt()方法第二個參數范圍的過程中發現,當傳入的值為1,基數為2時,也會返回1,這點需要注意一下。
我們再來看一下其他小例子:
1、將數組中的單詞轉換成復數形式。
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)); //返回["feet","geese","meese","kangareese"];
2、求數組中每個元素的平方根。
var numbers = [1, 4, 9]; var roots = number.map(Math.sqrt); //返回[1, 2, 3]
3、在字符串上使用map方法,實現如何在一個String上使用map方法獲取字符串中每個字符所對應的ASCII碼組成的數組:
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]
