本文寫做目的在於,對上次面試中未手寫出來的map函數作一個收尾工做。其內容以map函數做爲線,將其涉及到的衆多知識點穿針引線梳理一下,並賦予本人學習及寫做時的所感所想。既是所感所想,想必不免存在一些我的拙見,望各位大佬不吝指正,還望輕噴!!!node
還記得初識JS的map函數時webpack
[1,2,3].map((e, i, arr) => {
return 2*e; //[2,4,6]
})
複製代碼
大學簡單學過C語言後,第一次看到這個用法就感受特別神奇,徹底不知道它怎麼運做,僅對它造成了一個大體的輪廓:你想基於原數組生成一個怎樣的新數組,只要把邏輯寫在回調函數裏就行了。對我而言它徹底就像一個黑盒。但不覺間潛意識裏卻模糊了一些概念(已然與未然/主動與被動的關係)。web
以我我的入門JS的心路歷程來看,假若對如下知識有所涉獵瞭解,就算不是十分熟練也能輕鬆實現map函數。知識點以下: (僅以實現map而作簡單講解,詳情內容請自閱其餘文獻)面試
JS中基本數據類型和引用數據類型是不一樣的。當咱們把一個存儲基本數據類型值的變量A賦值給另外一個變量B時,本質是值傳遞,兩個變量存儲兩個獨立的值。但如果引用數據類型,本質是地址傳遞,那麼此時兩個變量存儲的是同一個數據的地址,所以A、B會互相影響。數據庫
咱們知道JS是一門多範式語言,其中就包括函數式編程。所以在JS中函數就像任何其餘引用數據類型同樣能夠把它們存在數組裏,看成函數參數傳遞,賦值給變量,做爲對象的屬性值等。編程
在JS中,當咱們用 var arr = [1,2,3] 建立一個數組並將其賦值給變量arr時,該方式本質上與var arr = new Array(1,2,3) 是沒有區別的。(這裏忽然意識到,還涉及到new構造函數調用的知識,優秀的你應該是知道該知識點的!!) 那麼此時arr表示的數組就能夠稱爲Array的一個實例,該實例的_proto_屬性是指向構造函數Array的原型對象(也就是Array.prototype所表示的一個對象)json
關於JS函數裏this的指向問題就再也不概述了,大體分爲四個規則加一個特殊的箭頭函數。如今對於 [1,2,3].map(callback) 咱們大概明白了,經過[1,2,3].map以原型鏈查詢的方式找到了在Array.prototype.map裏的函數,而後將callback函數做爲參數傳入map函數中以達到後期調用並執行相關邏輯的目的,可是咱們怎麼在調用的函數中找到原數組?沒錯經過函數中的this。數組
學習JS時才第一次接觸回調函數,一度以爲本身挺懂回調,後來發現本身真的是根本不懂,還覺得本身很懂!如今讓咱們看看map中回調的真容吧。promise
一直以來我都把回調函數理解成主動性,但事實上傳入的回調函數是被動性的。想一下平時咱們爲了實現某個功能定義了一個函數,而後傳參執行該函數。但回調函數本質只是一個函數聲明,之因此會執行相關的邏輯是由於以後會給該回調函數傳入參數並調用該回調函數,它是被調用的。bash
那麼這裏又涉及到已然性和未然性。原生的map函數是被定義過的,當調用map函數實現相關邏輯時,它內部執行流程就會將數組每一個元素的(item/元素值, index/元素索引, arr/原數組)傳入回調函數callback並以callback(item, index, arr)的形式調用。所以,咱們知道該回調會被傳入指定的參數並調用。因此,咱們在僅須要作的聲明傳入的回調函數時,能夠把此時回調函數的參數當作對應的數組中元素的值,在此基礎上實現相關邏輯。實際上,就是把聲明回調裏的參數當作map執行時內部調用回調時傳入的參數(item, index, arr)進行操做
其實以上兩點總結來講就是以往咱們都是先聲明函數,再傳參調用。而如今咱們在理解map函數時遇到的事實倒是,已經肯定了將回調函數傳入map中調用時,將會在map函數內調用該回調函數,且該回調函數是被傳入了固定參數的狀態下調用的。所以能夠說咱們已經肯定了內部會自動執行該回調,就差聲明回調並傳入map中執行了。因此如今對回調函數的理解是,會(hui)被調用的函數。
相信看完內容的你已經對map這個有了很清晰的認識了吧。其實我以爲若是以上能掌握,那麼之後絕大多數手寫方法的題應該都不成問題了。那麼接下來就讓我貼出手寫map的代碼吧。(寫文章真是個累人的活啊,貼出來把,寫不動了!)
Array.prototype.myMap = function(callback, context) {
var arr = this;
var res = [];
context = context ? context : window;
for(let i = 0; i < arr.length; i++) {
let tem = callback.call(context, arr[i], i, arr);
res.push(tem);
}
return res;
}
複製代碼
相信只要你稍微動動靈活的小腦殼確定也能實現一個reduce函數吧。
Array.prototype.myReduce = function(callback) {
var arr = this;
var res; <!--用arguments捕獲第二個參數由於其值多是null,NaN之類-->
if(typeof(callback) !== "function") throw new Error("not a function");
if(arguments.length < 2 && arr.length === 0) throw new Error("empty array with no initial value");
if(arguments.length < 2 && arr.length === 1) return arr[0];
if(arguments.length > 1 && arr.length === 0) return arguments[1];
res = arguments.length > 1? arguments[1] : arr.shift();
for(let i = 0; i < arr.length; i++) {
res = callback(res, arr[i], i, arr);
}
return res;
}
複製代碼
Array.prototype._myMap = function(callback, context) {
context = context ? context : window;
return this.reduce((accum, item, index, arr) =>
[...accum, callback.call(context, item, index, arr)]
, []);
}
複製代碼