摘要:函數(shù)作為參數(shù)情況,,和是中內(nèi)置的高階函數(shù)。知道了到底啊什么是高階函數(shù),有哪些類型的高階函數(shù)。公眾號技術(shù)棧路線大家好,我是,公眾號程序員成長指北作者,這篇文章是必知必會系列的高階函數(shù)講解。
前言
一道經(jīng)典面試題:
//JS實現(xiàn)一個無限累加的add函數(shù) add(1) //1 add(1)(2) //3 add(1)(2)(3) //6
當(dāng)大家看到這個面試題的時候,能否在第一時間想到使用高階函數(shù)實現(xiàn)?想到在實際項目開發(fā)過程中,用到哪些高級函數(shù)?有沒有想過自己創(chuàng)造一個高階函數(shù)呢?開始本篇文章的學(xué)習(xí)
文章已同步都github博客地址:
程序員成長指北技術(shù)棧博客地址
高階函數(shù)英文叫 Higher-order function。高階函數(shù)是對其他函數(shù)進(jìn)行操作的函數(shù),操作可以是將它們作為參數(shù),或者返回它們。簡單總結(jié)為高階函數(shù)是一個接收函數(shù)作為參數(shù)或者將函數(shù)作為返回輸出的函數(shù)。
函數(shù)作為參數(shù)情況Array.prototype.map,Array.prototype.filter,Array.prototype.reduce和Array.prototype.sort是JavaScript中內(nèi)置的高階函數(shù)。它們接受一個函數(shù)作為參數(shù),并應(yīng)用這個函數(shù)到列表的每一個元素。下面是一些內(nèi)置高階函數(shù)的具體說明講解,以及和不使用高階函數(shù)情況下的對比
Array.prototype.mapmap()(映射)方法最后生成一個新數(shù)組,不改變原始數(shù)組的值。其結(jié)果是該數(shù)組中的每個元素都調(diào)用一個提供的函數(shù)后返回的結(jié)果。
array.map(callback,[ thisObject]);
callback(回調(diào)函數(shù))
[].map(function(currentValue, index, array) { // ... });
傳遞給 map 的回調(diào)函數(shù)(callback)接受三個參數(shù),分別是currentValue——正在遍歷的元素、index(可選)——元素索引、array(可選)——原數(shù)組本身,除了 callback 之外還可以接受 this 值(可選),用于執(zhí)行 callback 函數(shù)時使用的this 值。
來個簡單的例子方便理解,現(xiàn)在有一個數(shù)組[1,2,3,4],我們想要生成一個新數(shù)組,其每個元素皆是之前數(shù)組的兩倍,那么我們有下面兩種使用高階和不使用高階函數(shù)的方式來實現(xiàn)。
不使用高階函數(shù)// koala const arr1 = [1, 2, 3, 4]; const arr2 = []; for (let i = 0; i < arr1.length; i++) { arr2.push( arr1[i] * 2); } console.log( arr2 ); // [2, 4, 6, 8] console.log( arr1 ); // [1, 2, 3, 4]使用高階函數(shù)
// kaola const arr1 = [1, 2, 3, 4]; const arr2 = arr1.map(item => item * 2); console.log( arr2 ); // [2, 4, 6, 8] console.log( arr1 ); // [1, 2, 3, 4]map高階函數(shù)注意點
callback需要有return值,否則會出現(xiàn)所有項映射為undefind;
// kaola const arr1 = [1, 2, 3, 4]; const arr2 = arr1.map(item => {}); console.log( arr2 ); // [ undefined, undefined, undefined, undefined ] console.log( arr1 ); // [1, 2, 3, 4]map高階函數(shù)對應(yīng)的一道經(jīng)典面試題
//輸出結(jié)果 ["1", "2", "3"].map(parseInt);
看了這道題不知道會不會有大多數(shù)開發(fā)者認(rèn)為輸出結(jié)果是[1,2,3],錯誤
正確的輸出結(jié)果為:
[1,NaN,NaN]
因為map的callback函數(shù)有三個參數(shù),正在遍歷的元素, 元素索引(index), 原數(shù)組本身(array)。parseInt有兩個參數(shù),string和radix(進(jìn)制),注意第二個參數(shù)進(jìn)制當(dāng)為0或者沒有參數(shù)的時候,parseInt()會根據(jù)string來判斷數(shù)字的基數(shù)。當(dāng)忽略參數(shù) radix , JavaScript 默認(rèn)數(shù)字的基數(shù)如下:
如果 string 以 "0x" 開頭,parseInt() 會把 string 的其余部分解析為十六進(jìn)制的整數(shù)。
如果 string 以 0 開頭,那么 ECMAScript v3 允許 parseInt() 的一個實現(xiàn)把其后的字符解析為八進(jìn)制或十六進(jìn)制的數(shù)字。
如果 string 以 1 ~ 9 的數(shù)字開頭,parseInt() 將把它解析為十進(jìn)制的整數(shù)。
只傳入parseInt的話,map callback會自動忽略第三個參數(shù)array。而index參數(shù)不會被忽略。而不被忽略的index(0,1,2)就會被parseInt當(dāng)做第二個參數(shù)。
將其拆開看:
parseInt("1",0);//上面說過第二個參數(shù)為進(jìn)制,所以"1",radix為0上面提到過,會忽略,根據(jù)string 以 1 ~ 9 的數(shù)字開頭,parseInt() 將把它解析為十進(jìn)制的整數(shù)1。 parseInt("2",1);//此時將2轉(zhuǎn)為1進(jìn)制數(shù),由于超過進(jìn)制數(shù)1,所以返回NaN。 parseInt("3",2);//此時將3轉(zhuǎn)為2進(jìn)制數(shù),由于超過進(jìn)制數(shù)1,所以返回NaN。
所以最終的結(jié)果為[1,NaN,NaN]。
那么如果想要得到[1,2,3]該怎么寫。
["1","2","3"].map((x)=>{ return parseInt(x); });
也可以簡寫為:
["1","2","3"].map(x=>parseInt(x));
這樣寫為什么就能返回想要的值呢?因為,傳一個完整函數(shù)進(jìn)去,有形參,有返回值。這樣就不會造成因為參數(shù)傳入錯誤而造成結(jié)果錯誤了,最后返回一個經(jīng)純函數(shù)處理過的新數(shù)組。
Array.prototype.reducereduce() 方法對數(shù)組中的每個元素執(zhí)行一個提供的 reducer 函數(shù)(升序執(zhí)行),將其結(jié)果匯總為單個返回值。傳遞給 reduce 的回調(diào)函數(shù)(callback)接受四個參數(shù),分別是累加器 accumulator、currentValue——正在操作的元素、currentIndex(可選)——元素索引,但是它的開始會有特殊說明、array(可選)——原始數(shù)組本身,除了 callback 之外還可以接受初始值 initialValue 值(可選)。
如果沒有提供 initialValue,那么第一次調(diào)用 callback 函數(shù)時,accumulator 使用原數(shù)組中的第一個元素,currentValue 即是數(shù)組中的第二個元素。 在沒有初始值的空數(shù)組上調(diào)用 reduce 將報錯。
如果提供了 initialValue,那么將作為第一次調(diào)用 callback 函數(shù)時的第一個參數(shù)的值,即 accumulator,currentValue 使用原數(shù)組中的第一個元素。
例子,現(xiàn)在有一個數(shù)組 [0, 1, 2, 3, 4],需要計算數(shù)組元素的和,需求比較簡單,來看下代碼實現(xiàn)。
不使用高階函數(shù)//koala const arr = [0, 1, 2, 3, 4]; let sum = 0; for (let i = 0; i < arr.length; i++) { sum += arr[i]; } console.log( sum ); // 10 console.log( arr ); // [0, 1, 2, 3, 4]使用高階函數(shù) 無 initialValue 值
const arr = [0, 1, 2, 3, 4]; let sum = arr.reduce((accumulator, currentValue, currentIndex, array) => { return accumulator + currentValue; }); console.log( sum ); // 10 console.log( arr ); // [0, 1, 2, 3, 4]
上面是沒有 initialValue 的情況,代碼的執(zhí)行過程如下,callback 總共調(diào)用四次。
callback | accumulator | currentValue | currentIndex | array | return value |
---|---|---|---|---|---|
first call | 0 | 1 | 1 | [0, 1, 2, 3, 4] | 1 |
second call | 1 | 2 | 2 | [0, 1, 2, 3, 4] | 3 |
third call | 3 | 3 | 3 | [0, 1, 2, 3, 4] | 6 |
fourth call | 6 | 4 | 4 | [0, 1, 2, 3, 4] | 10 |
我們再來看下有 initialValue 的情況,假設(shè) initialValue 值為 10,我們看下代碼。
//koala const arr = [0, 1, 2, 3, 4]; let sum = arr.reduce((accumulator, currentValue, currentIndex, array) => { return accumulator + currentValue; }, 10); console.log( sum ); // 20 console.log( arr ); // [0, 1, 2, 3, 4]
代碼的執(zhí)行過程如下所示,callback 總共調(diào)用五次。
callback | accumulator | currentValue | currentIndex | array | return value |
---|---|---|---|---|---|
first call | 10 | 0 | 0 | [0, 1, 2, 3, 4] | 10 |
second call | 10 | 1 | 1 | [0, 1, 2, 3, 4] | 11 |
third call | 11 | 2 | 2 | [0, 1, 2, 3, 4] | 13 |
fourth call | 13 | 3 | 3 | [0, 1, 2, 3, 4] | 16 |
fifth call | 16 | 4 | 4 | [0, 1, 2, 3, 4] | 20 |
filter(過濾,篩選) 方法創(chuàng)建一個新數(shù)組,原始數(shù)組不發(fā)生改變。
array.filter(callback,[ thisObject]);
其包含通過提供函數(shù)實現(xiàn)的測試的所有元素。接收的參數(shù)和 map 是一樣的,filter的callback函數(shù)需要返回布爾值true或false. 如果為true則表示通過啦!如果為false則失敗,其返回值是一個新數(shù)組,由通過測試為true的所有元素組成,如果沒有任何數(shù)組元素通過測試,則返回空數(shù)組。
來個例子介紹下,現(xiàn)在有一個數(shù)組 [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4],我們想要生成一個新數(shù)組,這個數(shù)組要求沒有重復(fù)的內(nèi)容,即為去重。
不使用高階函數(shù)const arr1 = [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4]; const arr2 = []; for (let i = 0; i < arr1.length; i++) { if (arr1.indexOf( arr1[i] ) === i) { arr2.push( arr1[i] ); } } console.log( arr2 ); // [1, 2, 3, 5, 4] console.log( arr1 ); // [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4]使用高階函數(shù)
const arr1 = [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4]; const arr2 = arr1.filter( (element, index, self) => { return self.indexOf( element ) === index; }); console.log( arr2 ); // [1, 2, 3, 5, 4] console.log( arr1 ); // [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4]filter注意點說明
callback在過濾測試的時候,一定要是Boolean值嗎?
例子:
var arr = [0, 1, 2, 3]; var arrayFilter = arr.filter(function(item) { return item; }); console.log(arrayFilter); // [1, 2, 3]
通過例子可以看出:過濾測試的返回值只要是弱等于== true/false就可以了,而非非得返回 === true/false.
Array.prototype.sortsort() 方法用原地算法對數(shù)組的元素進(jìn)行排序,并返回數(shù)組,該排序方法會在原數(shù)組上直接進(jìn)行排序,并不會生成一個排好序的新數(shù)組。排序算法現(xiàn)在是穩(wěn)定的。默認(rèn)排序順序是根據(jù)字符串Unicode碼點。
// 語法 arr.sort([compareFunction])
compareFunction參數(shù)是可選的,用來指定按某種順序進(jìn)行排列的函數(shù)。注意該函數(shù)有兩個參數(shù):
參數(shù)1:firstEl
第一個用于比較的元素。
參數(shù)2:secondEl
第二個用于比較的元素??聪旅娴睦优c說明:
// 未指明compareFunction函數(shù) ["Google", "Apple", "Microsoft"].sort(); // ["Apple", "Google", "Microsoft"]; // apple排在了最后: ["Google", "apple", "Microsoft"].sort(); // ["Google", "Microsoft", "apple"] // 無法理解的結(jié)果: [10, 20, 1, 2].sort(); // [1, 10, 2, 20] //正確的結(jié)果 [6, 8, 1, 2].sort(); // [1, 2,6, 8] // 指明compareFunction函數(shù) "use strict"; var arr = [10, 20, 1, 2]; arr.sort(function (x, y) { if (x < y) { return -1; } if (x > y) { return 1; } return 0; }); console.log(arr); // [1, 2, 10, 20]
如果沒有指明 compareFunction ,那么元素會按照轉(zhuǎn)換為的字符串的諸個字符的Unicode位點進(jìn)行排序。例如 "Banana" 會被排列到 "cherry" 之前。當(dāng)數(shù)字按由小到大排序時,10 出現(xiàn)在 2 之前,但因為(沒有指明 compareFunction),比較的數(shù)字會先被轉(zhuǎn)換為字符串,所以在Unicode順序上 "10" 要比 "2" 要靠前。
如果指明了 compareFunction ,那么數(shù)組會按照調(diào)用該函數(shù)的返回值排序。即 a 和 b 是兩個將要被比較的元素:
如果 compareFunction(a, b) 小于 0 ,那么 a 會被排列到 b 之前;
如果 compareFunction(a, b) 等于 0 , a 和 b 的相對位置不變。備注: ECMAScript 標(biāo)準(zhǔn)并不保證這一行為,而且也不是所有瀏覽器都會遵守(例如 Mozilla 在 2003 年之前的版本);
如果 compareFunction(a, b) 大于 0 , b 會被排列到 a 之前。
compareFunction(a, b) 必須總是對相同的輸入返回相同的比較結(jié)果,否則排序的結(jié)果將是不確定的。
sort排序算法的底層實現(xiàn)看了上面sort的排序介紹,我想小伙伴們肯定會對sort排序算法的內(nèi)部實現(xiàn)感興趣,我在sf上面搜了一下,發(fā)現(xiàn)有些爭議。于是去查看了V8引擎的源碼,發(fā)現(xiàn)在源碼中的710行
源碼地址:https://github.com/v8/v8/blob...
// In-place QuickSort algorithm. // For short (length <= 22) arrays, insertion sort is used for efficiency.
V8 引擎 sort 函數(shù)只給出了兩種排序 InsertionSort和 QuickSort,數(shù)量小于等于22的數(shù)組使用 InsertionSort,比22大的數(shù)組則使用 QuickSort,有興趣的可以看看具體算法實現(xiàn)。
注意:不同的瀏覽器引擎可能算法實現(xiàn)并不同,我這里只是查看了V8引擎的算法實現(xiàn),有興趣的小伙伴可以查看下其他開源瀏覽器具體sort的算法實現(xiàn)。如何改進(jìn)排序算法實現(xiàn)數(shù)字正確排序呢?
對于要比較數(shù)字而非字符串,比較函數(shù)可以簡單的以 a 減 b,如下的函數(shù)將會將數(shù)組升序排列,降序排序則使用b-a。
let compareNumbers= function (a, b) { return a - b; } let koala=[10, 20, 1, 2].sort(compareNumbers) console.log(koala); // [1 , 2 , 10 , 20]函數(shù)作為返回值輸出
返回一個函數(shù),下面直接看兩個例子來加深理解。
isType 函數(shù)我們知道在判斷類型的時候可以通過Object.prototype.toString.call 來獲取對應(yīng)對象返回的字符串,比如:
let isString = obj => Object.prototype.toString.call( obj ) === "[object String]"; let isArray = obj => Object.prototype.toString.call( obj ) === "[object Array]"; let isNumber = obj => Object.prototype.toString.call( obj ) === "[object Number]";
可以發(fā)現(xiàn)上面三行代碼有很多重復(fù)代碼,只需要把具體的類型抽離出來就可以封裝成一個判斷類型的方法了,代碼如下。
let isType = type => obj => { return Object.prototype.toString.call( obj ) === "[object " + type + "]"; } isType("String")("123"); // true isType("Array")([1, 2, 3]); // true isType("Number")(123); // true
這里就是一個高階函數(shù),因為 isType 函數(shù)將 obj => { ... } 這一函數(shù)作為返回值輸出。
add求和函數(shù)前言中的面試題,用 JS 實現(xiàn)一個無限累加的函數(shù) add,示例如下:
add(1); // 1 add(1)(2); // 3 add(1)(2)(3); // 6
分析面試題的結(jié)構(gòu),都是將函數(shù)作為返回值輸出,然后接收新的參數(shù)并進(jìn)行計算。
我們知道打印函數(shù)時會自動調(diào)用 toString()方法(如果不知道的可以去看我的這篇文章),函數(shù) add(a) 返回一個sum(b)函數(shù),函數(shù) sum() 中累加計算 a = a + b,只需要重寫sum.toString()方法返回變量 a 就可以了。
function add(a) { function sum(b) { // 使用閉包 a = a + b; // 累加 return sum; } sum.toString = function() { // 重寫toString()方法 return a; } return sum; // 返回一個函數(shù) } add(1); // 1 add(1)(2); // 3 add(1)(2)(3); // 6如何自己創(chuàng)建高階函數(shù)
前面講了語言中內(nèi)置的各種高階函數(shù)。知道了到底啊什么是高階函數(shù),有哪些類型的高階函數(shù)。那么讓我們自己創(chuàng)建一個高階函數(shù)吧!
假設(shè) JavaScript 沒有原生的 map 方法。 我們自己構(gòu)建個類似map的高階函數(shù),從而創(chuàng)建我們自己的高階函數(shù)。
假設(shè)我們有一個字符串?dāng)?shù)組,我們希望把它轉(zhuǎn)換為整數(shù)數(shù)組,其中每個元素代表原始數(shù)組中字符串的長度。
const strArray=["JavaScript","PHP","JAVA","C","Python"]; function mapForEach(arr,fn){ const newArray = []; for(let i = 0; i代碼分析講解: 我們創(chuàng)建了一個高階函數(shù) mapForEach ,它接受一個數(shù)組和一個回調(diào)函數(shù) fn。 它循環(huán)遍歷傳入的數(shù)組,并在每次迭代時在 newArray.push 方法調(diào)用回調(diào)函數(shù) fn 。
回調(diào)函數(shù) fn 接收數(shù)組的當(dāng)前元素并返回該元素的長度,該元素存儲在 newArray 中。 for 循環(huán)完成后,newArray 被返回并賦值給 lenArray。
總結(jié)我們已經(jīng)了解了高階函數(shù)和一些內(nèi)置的高階函數(shù),還學(xué)習(xí)了如何創(chuàng)建自己的高階函數(shù)。簡而言之,高階函數(shù)是一個可以接收函數(shù)作為參數(shù),甚至返回一個函數(shù)的函數(shù)。 它就像常規(guī)函數(shù)一樣,只是多了接收和返回其他函數(shù)的附加能力,即參數(shù)和輸出。
公眾號技術(shù)棧路線大家好,我是koala,公眾號「程序員成長指北」作者,這篇文章是【JS必知必會系列】的高階函數(shù)講解。目前在做一個node后端高級工程師進(jìn)階路線,加入我們一起學(xué)習(xí)吧!
加入我們
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/105757.html
摘要:哪吒別人的看法都是狗屁,你是誰只有你自己說了才算,這是爹教我的道理。哪吒去他個鳥命我命由我,不由天是魔是仙,我自己決定哪吒白白搭上一條人命,你傻不傻敖丙不傻誰和你做朋友太乙真人人是否能夠改變命運,我不曉得。我只曉得,不認(rèn)命是哪吒的命。 showImg(https://segmentfault.com/img/bVbwiGL?w=900&h=378); 出處 查看github最新的Vue...
摘要:通過請求和響應(yīng)的交換達(dá)成通信協(xié)議中已經(jīng)規(guī)定了請求是從客戶端發(fā)出,最后由服務(wù)端響應(yīng)這個請求并返回。隨后的字符串指明了請求訪問的資源對象。協(xié)議自身不對請求和響應(yīng)之間的通信狀態(tài)進(jìn)行保存,也就是說這個級別。從前發(fā)送請求后需等待并受到響應(yīng)。 showImg(https://segmentfault.com/img/bVbmDsG?w=1024&h=538); http協(xié)議用戶客戶端和服務(wù)器之間的...
摘要:歡迎來我的個人站點性能優(yōu)化其他優(yōu)化瀏覽器關(guān)鍵渲染路徑開啟性能優(yōu)化之旅高性能滾動及頁面渲染優(yōu)化理論寫法對壓縮率的影響唯快不破應(yīng)用的個優(yōu)化步驟進(jìn)階鵝廠大神用直出實現(xiàn)網(wǎng)頁瞬開緩存網(wǎng)頁性能管理詳解寫給后端程序員的緩存原理介紹年底補(bǔ)課緩存機(jī)制優(yōu)化動 歡迎來我的個人站點 性能優(yōu)化 其他 優(yōu)化瀏覽器關(guān)鍵渲染路徑 - 開啟性能優(yōu)化之旅 高性能滾動 scroll 及頁面渲染優(yōu)化 理論 | HTML寫法...
摘要:歡迎來我的個人站點性能優(yōu)化其他優(yōu)化瀏覽器關(guān)鍵渲染路徑開啟性能優(yōu)化之旅高性能滾動及頁面渲染優(yōu)化理論寫法對壓縮率的影響唯快不破應(yīng)用的個優(yōu)化步驟進(jìn)階鵝廠大神用直出實現(xiàn)網(wǎng)頁瞬開緩存網(wǎng)頁性能管理詳解寫給后端程序員的緩存原理介紹年底補(bǔ)課緩存機(jī)制優(yōu)化動 歡迎來我的個人站點 性能優(yōu)化 其他 優(yōu)化瀏覽器關(guān)鍵渲染路徑 - 開啟性能優(yōu)化之旅 高性能滾動 scroll 及頁面渲染優(yōu)化 理論 | HTML寫法...
摘要:歡迎來我的個人站點性能優(yōu)化其他優(yōu)化瀏覽器關(guān)鍵渲染路徑開啟性能優(yōu)化之旅高性能滾動及頁面渲染優(yōu)化理論寫法對壓縮率的影響唯快不破應(yīng)用的個優(yōu)化步驟進(jìn)階鵝廠大神用直出實現(xiàn)網(wǎng)頁瞬開緩存網(wǎng)頁性能管理詳解寫給后端程序員的緩存原理介紹年底補(bǔ)課緩存機(jī)制優(yōu)化動 歡迎來我的個人站點 性能優(yōu)化 其他 優(yōu)化瀏覽器關(guān)鍵渲染路徑 - 開啟性能優(yōu)化之旅 高性能滾動 scroll 及頁面渲染優(yōu)化 理論 | HTML寫法...
閱讀 2108·2021-11-15 17:57
閱讀 789·2021-11-11 16:54
閱讀 2633·2021-09-27 13:58
閱讀 4220·2021-09-06 15:00
閱讀 990·2021-09-04 16:45
閱讀 3541·2019-08-30 15:56
閱讀 1810·2019-08-30 15:53
閱讀 1686·2019-08-30 14:12