函數(shù)式編程

為什么學(xué)習(xí)函數(shù)式編程

  • 函數(shù)式編程隨著react的流行受到越來越多的關(guān)注
  • vue3也開始擁抱函數(shù)式編程
  • 函數(shù)式編程可以拋棄this
  • 打包過程中可以更好的利用tree shaking過濾無用代碼
  • 方便測試籍嘹,方便并行處理
    很多庫可以幫助我們進行函數(shù)式開發(fā):lodash,underscore,remda

函數(shù)相關(guān)復(fù)習(xí)

函數(shù)是一等公民


把函數(shù)賦值給變量

//把函數(shù)賦值給變量
let fn=function(){
  console.log("hello")
}
fn()

//一個事例
const blogController={
  index(props){return views.index(props)},
  show(props){return views.show(props)},
  create(props){return views.create(props)},
  update(props){return views.update(props)},
  destroy(props){return views.destroy(props)},
 }
//優(yōu)化
const blogController={
  index:views.index,
  show:views.show,
  create:views.create,
  update:views.update,
  destroy :views.destroy
 }

高階函數(shù)


高階函數(shù)-函數(shù)作為參數(shù)

function forEach(array ,fn){
 for(let i=0;i<array.length;i++){
    fn(array[i])
 }
}
let arr=[1,2,3,5]
forEach(arr,(item)=>{
  console.log(item)
})

function filter(array,fn){
  let results=[]
  for(let i=0;i<array.length;i++){
    if(fn(arrat[i])){
      results.push(array[i])
    }
 }
}
let arr=[1,2,3,5]
let res = filter(arr,()=>{
  return item % 2 === 0
})

高階函數(shù)-函數(shù)作為返回值

function makeFn(){
  let msg='hello'
  return function(){
    console.log(msg)
  }
}
makeFn()() //"hello"

function once(fn){
  let done = false
  return function(){
    if(!done){
      done=true
       fn.apply(this,arguments)
    }
  }
}
let pay=once(function (money){
  console.log(`支付:${money}rmb`)
})

//模擬常用高階函數(shù)map黑忱,every,some
const map=(array,fn)=>{
  let results=[]
  for(let value of array){
    results.push(fn(value))
 }
  return results
}

const every=(array,fn)=>{
  let result=true
  for(let value of array){
    result=fn(value)
    if(!result) break
 }
  return result
}

const some=(array,fn)=>{
  let result=false
  for(let value of array){
    result=fn(value)
    if(result) break
 }
  return result
}

let arr=[1,5,6,8]
arr=map(arr,v=>v*v)
let res=every(arr,v=>v>10)
let r=some(arr,v=>v % 2 === 0)

閉包


可以在另一個作用域中調(diào)用一個函數(shù)內(nèi)部函數(shù)并訪問到該函數(shù)的作用域中的成員

function makeFn(){
  let mes="hello"
  return function(){
    console.log(msg)
  }
}
const fn=makeFn() 
fn()

閉包的本質(zhì):函數(shù)在執(zhí)行的時候會放到執(zhí)行棧上當(dāng)函數(shù)執(zhí)行完畢后會從執(zhí)行棧上移除匾灶,但堆上的作用域成員因為被外部引用不能釋放瞬逊,因此內(nèi)部函數(shù)依然可以訪問外部函數(shù)的成員。

函數(shù)式編程基礎(chǔ)

純函數(shù)

純函數(shù)必須有參數(shù)和返回值蒙挑,并且相同的輸入始終會得到相同的輸出,而且沒有任何可觀察的副作用

let arr=[1,2,3,4,5]
//多次調(diào)用
arr.slice(0,3)//輸出相同愚臀,純函數(shù)
arr.splice(0,3)//輸出不同忆蚀,不純


  • 函數(shù)式編程不會保留計算中間的結(jié)果,所以變量是不可變的
  • 我們可以吧一個函數(shù)的執(zhí)行結(jié)果交給另一個函數(shù)去處理

lodash純函數(shù)的代表

const _=require('lodash')
const array=['jack','bob','tim','lucy']

純函數(shù)的好處
  • 可緩存姑裂,提高函數(shù)性能
const _=require('lodash')

function getArea(r){
  return MathPI*r*r
}

//模擬memoize
function memoize(fn)
  let cache={}
  return function(){
    let key = JOSN.stringify(arguments)
    cache[key]=cache[key]||f.apply(f,arguments)
    return cache[key]
  }
}
  • 可測試

  • 并行處理
    1.多線程環(huán)境下操作共享內(nèi)存數(shù)據(jù)很可能出現(xiàn)意外情況
    2.純函數(shù)不需要訪問共享的內(nèi)存數(shù)據(jù)馋袜,所以在并行環(huán)境可一任意運行純函數(shù)

柯里化

function checkAge(min){
  return function (age){
    return age>=min
  }
}
let checkAge=min=>(age=>age>=min)
let check18=checkAge(18)
let check20=checkAge(20)
console.log(check18(24))

function curry(fun){
  return function curriedFn(...args){
    if(args.length<func.length){
      return function(){
        return curriedFn(...args.concant(Array.from(arguments)))
      }
    }
  }
}
  • 當(dāng)一個函數(shù)有多個參數(shù)的時候先傳遞一部分參數(shù)調(diào)用他
  • 然后返回一個新的函數(shù)接收剩余的參數(shù),返回結(jié)果

柯里化總結(jié)

  • 柯里化可以讓我們給一個函數(shù)傳遞較少的參數(shù)得到一個已經(jīng)記住某些固定參數(shù)的新函數(shù)
  • 這是一種對函數(shù)參數(shù)的緩存
  • 讓函數(shù)變得更加靈活舶斧,讓函數(shù)的粒度更小
  • 可以吧多元函數(shù)轉(zhuǎn)化成一元函數(shù)欣鳖,可以組合使用函數(shù)產(chǎn)生強大的功能

函數(shù)組合

  • 概念:如果一個函數(shù)要經(jīng)過多個函數(shù)處理才能得到最終的結(jié)果,這時候可以吧中間過程的函數(shù)合并成一個函數(shù)
  • 函數(shù)組合默認(rèn)是從右到左執(zhí)行
function compose(){}

lodash中FP模塊-函數(shù)式編程柯里化后的模塊
如果有多個參數(shù)時茴厉,函數(shù)優(yōu)先數(shù)據(jù)之后原則

函子

函子Functor

容器:包含值和值的變形關(guān)系
函子:是個特殊容器泽台,通過一個普通對象來實現(xiàn),該對象具有map方法矾缓,map方法可以運行一個函數(shù)對值進行處理

class Container{
  static of(value){
    return new Container(value)
  }
  constructor(value){
    this._value=value
  }
  
  map(fn){
    return Container.of(fn(this._value)
  }
}
let r=Container.of(5).map(x=>x+1).map(x=>x*x)
console.log(r)//Container { _value: 36 }

總結(jié)

  • 函數(shù)是編程運算不直接操作值师痕,而是由函子完成
  • 函子就是實現(xiàn)了map契約的對象
  • 我們可以把函子想象成盒子,盒子里面封裝了一個值
  • 想要處理盒子重的值而账,我們需要給盒子的map方法專遞一個處理值的函數(shù)(純函數(shù))胰坟,由這個函數(shù)對值進行處理
  • 最終map方法返回一個包含新值的盒子(函子)

MayBe函子

class MayBe{
  static of(value){
    return new MayBe(value)
  }
  constructor(value){
    this._value=value
  }
  
  map(fn){
    return this.isNothing() ? MayBe.of(null) : MayBe.of(fn(this._value)
  }

  isNothing(){
    return this._value === null || this._value === undifined
  }
}

Either函子

  • Either兩者中的任何一個,類似于if...else...的處理
  • 異常會讓函數(shù)變得不純泞辐,Either函子可以用來做異常處理
class Left{
  static of(value){
    return new Left(value)
  }
  constructor(value){
    this._value=value
  }
  
  map(fn){
    return this
  }
}

class Right{
  static of(value){
    return new Right(value)
  }
  constructor(value){
    this._value=value
  }
  
  map(fn){
    return Right.of(fn(this._value)
  }
}

function parseJSON(str){
   try{
    return Right.of(JSON.parse(str))
   }catch(e){
    return Left.of({error:e.message})
   }
}

IO函子

  • IO函子中的_value是一個函數(shù)笔横,這里是把函數(shù)作為值來處理
  • IO函子可以把不純的動作存儲到_value中,延遲執(zhí)行這個不純的操作
  • 把不純的操作交給調(diào)用者來處理
const  fp= require('lodash/fp')
class IO{
  static of(value){
    return new IO(function(){
      return value
    })
  }
  constructor(fn){
    this._value=fn
  }
  
  map(fn){
    return new IO(fp.flowRight(fn.this._value))
  }
}
let r =IO.of(process).map(p=>p.execPath)
console.log(r._value())

Task函子

const fs =require('fs')
const {task} =require('folktale/cocurrency.task')
const {spilt,find}=require('lodash/fp')

function readFile(filename){
  fs.readFile(filename,"utf-8",(err,data)=>{
    if(err) resolver.reject(err)
    resolver.resolve(data)
  })
}
readFile('pakage.json')
.map(split(''/n'))
.map(find(x=>x.inclues('version')))
.run()
.listen({
  onRejected:err=>{},
  onResolved:value=>{}
})

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末咐吼,一起剝皮案震驚了整個濱河市吹缔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌锯茄,老刑警劉巖厢塘,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茶没,死亡現(xiàn)場離奇詭異,居然都是意外死亡晚碾,警方通過查閱死者的電腦和手機抓半,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來格嘁,“玉大人笛求,你說我怎么就攤上這事「獠荆” “怎么了探入?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長懂诗。 經(jīng)常有香客問我蜂嗽,道長,這世上最難降的妖魔是什么殃恒? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任徒爹,我火速辦了婚禮,結(jié)果婚禮上芋类,老公的妹妹穿的比我還像新娘。我一直安慰自己界阁,他們只是感情好侯繁,可當(dāng)我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著泡躯,像睡著了一般贮竟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上较剃,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天咕别,我揣著相機與錄音,去河邊找鬼写穴。 笑死惰拱,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的啊送。 我是一名探鬼主播偿短,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼馋没!你這毒婦竟也來了昔逗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤篷朵,失蹤者是張志新(化名)和其女友劉穎勾怒,沒想到半個月后婆排,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡笔链,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年段只,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卡乾。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡翼悴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出幔妨,到底是詐尸還是另有隱情鹦赎,我是刑警寧澤,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布误堡,位于F島的核電站古话,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏锁施。R本人自食惡果不足惜陪踩,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望悉抵。 院中可真熱鬧肩狂,春花似錦、人聲如沸姥饰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽列粪。三九已至审磁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間岂座,已是汗流浹背态蒂。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留费什,地道東北人钾恢。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像鸳址,于是被迫代替她去往敵國和親赘那。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,697評論 2 351