高階函數(shù)
概念
Javascript中的函數(shù)本質(zhì)上都指向某個(gè)變量,既然變量可以指向函數(shù)董朝,函數(shù)的參數(shù)可以接受變量找蜜,那么函數(shù)是不是可以可以作為另一個(gè)函數(shù)的入?yún)ⅲ恳驗(yàn)镴avascript是一門弱類型語(yǔ)言婚被,不會(huì)對(duì)函數(shù)輸入值和函數(shù)的輸出值進(jìn)行強(qiáng)定義和類型檢查狡忙。所以函數(shù)可以作為參數(shù),也可以作為輸出值址芯。
一個(gè)最簡(jiǎn)單的高階函數(shù):
let add=(x,y,f)=>f(x)+f(y);
對(duì)其他函數(shù)進(jìn)行操作的函數(shù)灾茁,可以將他們作為參數(shù)或返回它們窜觉。
高階函數(shù)分類
- 函數(shù)作為參數(shù)傳遞
- 函數(shù)作為返回值
map/reduce/filter
map
定義:map() 方法返回一個(gè)新數(shù)組,不會(huì)改變?cè)紨?shù)組北专。同時(shí)新數(shù)組中的元素為原始數(shù)組元素調(diào)用函數(shù)處理后的值禀挫,并按照原始數(shù)組元素順序依次處理元素。
語(yǔ)法:array.map(function(currentValue /*必填拓颓,當(dāng)前元素的值 */,index /*可選语婴,當(dāng)前元素的索引*/,arr /*可選,當(dāng)前元素屬于的數(shù)組對(duì)象*/), thisValue /*可選驶睦,上下文對(duì)象砰左,用作this,默認(rèn)為全局對(duì)象*/)
有以下需求:創(chuàng)建一個(gè)新數(shù)組场航,其中的值是原數(shù)組的值的兩倍缠导。
const arr=[1,2,3]
let fn=(arr)=>{
let temp=[]
for(let i=0;i<arr.length;i++){
temp.push(arr[i]*2)
}
return temp;
}
console.log(fn(arr))
以上是我們的命令式編程,我們改寫下:
const temp=[1,2,3].map(item=>item*2)
console.log(temp)
在這里我們使用(item)=>item*2
作為map函數(shù)的參數(shù)旗闽。
手動(dòng)實(shí)現(xiàn)map()
酬核,遍歷函數(shù)的每一項(xiàng)作為函數(shù)的入?yún)ⅲ⒎祷孛看魏瘮?shù)調(diào)用結(jié)果組成的數(shù)組
關(guān)鍵:1.函數(shù)為空适室,返回空 2.func 必須是函數(shù)類型嫡意,需要有return值,否則會(huì)出現(xiàn)所有項(xiàng)映射為undefind 3.無(wú)副作用
Array.prototype.mapTest = function (func, content) {
if (typeof func !== 'function') throw new TypeError(
`${typeof func} is not a function`)
let arrTemp = this.slice()
content = typeof content === 'undefined' ? null : content
let result = []
for (let idx = 0; idx < arrTemp.length; idx++) {
result.push(func.call(content, arrTemp[idx], idx, arrTemp))
}
return result
}
reduce
定義:reduce() 方法接收一個(gè)函數(shù)作為累加器捣辆,數(shù)組中的每個(gè)值(從左到右)開(kāi)始縮減蔬螟,最終計(jì)算為一個(gè)值
語(yǔ)法:array.reduce(function(total /*必需。初始值, 或者計(jì)算結(jié)束后的返回值*/, currentValue/*必需汽畴。當(dāng)前元素*/, currentIndex/*可選旧巾。當(dāng)前元素的索引*/, arr/*可選。當(dāng)前元素所屬的數(shù)組對(duì)象忍些。*/), initialValue/*可選鲁猩。傳遞給函數(shù)的初始值*/)
有以下需求:對(duì)數(shù)組arr的項(xiàng)進(jìn)行求和
let arr=[1,2,3,4]
// 命令式編程
let sum=arr=>{
let temp=0;
for(let idx=0;idx<arr.length;idx++){
temp+=arr[idx]
}
return temp
}
//使用reduce
let total=arr.reduce((prev,cur)=>{
return prev+cur
},0)
過(guò)程分析:
第一次執(zhí)行時(shí),prev
的值為我們的初始值0罢坝,返回0+cur
(當(dāng)前數(shù)組的第一位)作為第二次執(zhí)行時(shí)的prev
….從而達(dá)到累加的目的廓握;
手動(dòng)實(shí)現(xiàn)一個(gè)reduce()
,核心就是遍歷數(shù)組每一項(xiàng)參與函數(shù)運(yùn)算并將返回值作為下次遍歷的初始值嘁酿,從而實(shí)現(xiàn)累加隙券。
關(guān)鍵點(diǎn):1.無(wú)初始值時(shí),從原數(shù)組的第二項(xiàng)進(jìn)行遍歷 2.無(wú)副作用
Array.prototype.reduceTest = function(func, initVal, directionLeft = true) {
if (typeof func !== 'function') throw new TypeError(`${typeof func} is not a function`)
if (!this.length) {
if (typeof initVal !== 'undefined') {
return initVal
} else {
throw new Error('Reduce of empty array with no initial value')
}
}
let array = this.slice()
let hasInitVal = typeof initVal !== 'undefined'
let value = hasInitVal ? initVal : array[0]
if (directionLeft === false) {
array = array.reverse()
}
for (let idx = hasInitVal ? 0 : 1; idx < array.length; idx++) {
const cur = array[idx]
value = func.call(null,value, cur, idx, array)
}
return value
}
filter
定義:filter() 方法創(chuàng)建一個(gè)新的數(shù)組闹司,新數(shù)組中的元素是通過(guò)檢查指定數(shù)組中符合條件的所有元素娱仔。
語(yǔ)法:array.filter(function(currentValue/*必須。當(dāng)前元素的值*/,index/*可選游桩。當(dāng)前元素的索引值*/,arr/*可選牲迫。當(dāng)前元素屬于的數(shù)組對(duì)象*/), thisValue/*可選耐朴。對(duì)象作為該執(zhí)行回調(diào)時(shí)使用,傳遞給函數(shù)盹憎,用作 "this" 的值*/)
需求:過(guò)濾出對(duì)象數(shù)組中age大于10的元素
let persons = [
{ 'name': 'Bob', age: 12 },
{ 'name': 'Lily', age: 13 },
{ 'name': 'Tom', age: 9 },
{ 'name': 'Jone', age: 99 },
]
// 命令式編程
let result=()=>{
let res=[];
for(let idx=0;idx<persons.length;idx++){
if(persons[idx].age>10){
res.push(persons[idx])
}
}
return res;
}
// 使用filter
let filterResult=persons.filter(item=>item.age>9);
手動(dòng)實(shí)現(xiàn)一個(gè)filter()
隔箍,核心就是創(chuàng)建一個(gè)新的數(shù)組,新數(shù)組中的元素是原數(shù)組中符合條件的項(xiàng)脚乡。
關(guān)鍵點(diǎn):1.函數(shù)返回值為true/false 2.無(wú)副作用
Array.prototype.filterTest=function (func,content) {
if (typeof func !== 'function') throw new TypeError(`${typeof func} is not a function`)
if (!this.length) return []
let arr = this.slice()
content = typeof content === 'undefined' ? null : content
let result=[]
for(let idx=0;idx<arr.length;idx++){
let res=func.call(content,arr[idx],idx,arr)
if(res){
result.push(arr[idx])
}
}
return result
}
flat
注意:普通瀏覽器(僅Chrome v69,F(xiàn)irefox Nightly和Opera 56等)/Node.js (需V11.0.0及以上) 尚未實(shí)現(xiàn)flat
方法滨达。這是一項(xiàng)實(shí)驗(yàn)性功能奶稠。 [MDN 說(shuō)明以及替代方案]
用法:將目標(biāo)嵌套數(shù)組扁平化,變?yōu)樾碌囊痪S數(shù)組
語(yǔ)法:arrry.flat([depth]/*指定要提取嵌套數(shù)組的結(jié)構(gòu)深度捡遍,默認(rèn)值為 1锌订。Infinity為無(wú)限*/)
手動(dòng)實(shí)現(xiàn)一個(gè)flat()
/**
* 數(shù)組扁平化
* @param depth 拉平深度 默認(rèn)為1,最大為Infinity
*/
Array.prototype.flatTest = function (depth = 1) {
let arr = this.slice()
const result = []
// 當(dāng)前已拉平層數(shù)
let flattenDepth = 1;
// 先拉平第一層
(function flatten (list) {
list.forEach((item) => {
if (flattenDepth < depth &&Array.isArray(item)) {
flattenDepth++
flatten(item)
} else {
result.push(item)
}
})
})(arr)
return result
}
總結(jié)
高階函數(shù)本質(zhì)上就是對(duì)算法的高度抽象,通過(guò)提高抽象度画株,實(shí)現(xiàn)最大程度代碼重構(gòu)辆飘。