函數(shù)式編程

函數(shù)式編程是一種編程范式,和面向?qū)ο缶幊坛什⒘嘘P(guān)系涩堤。

  • 面向?qū)ο缶幊蹋簩ΜF(xiàn)實(shí)世界中事物的抽象,抽象出對象以及對象和對象之間的關(guān)系;
  • 函數(shù)式編程:把現(xiàn)實(shí)世界事物和事物之間的聯(lián)系抽象到程序世界(對運(yùn)算過程進(jìn)行抽象)娃循。

推薦書籍

  • 你不知道Javascript
  • Javascript忍者秘籍
  • Javascript20years

函數(shù)是一等公民

  • 函數(shù)可以存儲(chǔ)在變量中;
  • 函數(shù)作為參數(shù)斗蒋;
  • 函數(shù)作為返回值捌斧。

原理:在js中函數(shù)的數(shù)據(jù)類型為對象。

高階函數(shù)

可以把函數(shù)作為參數(shù)/返回值泉沾。

意義:屏蔽細(xì)節(jié)捞蚂,便于抽象。

常用的高階函數(shù):

  • forEach
  • map
  • filter
  • every:檢測數(shù)組中的所有元素是否符合條件跷究,有一個(gè)不符合返回false
  • some:檢測數(shù)組中是否函數(shù)符合條件姓迅,有一個(gè)符合返回true
  • find/findIndex:返回?cái)?shù)組中滿足條件的第一個(gè)元素的值/索引,沒有則返回undefined、-1
  • reduce:接收一個(gè)函數(shù)作為累加器丁存,數(shù)組中的值從左到右肩杈,上一個(gè)輸出作為下一次迭代的輸入,最后返回一個(gè)值解寝±┤唬可以作為一個(gè)高階函數(shù)用于函數(shù)組合
  • sort

this指向

改變函數(shù)this指向?qū)Ψ椒ǎ篵ind(不調(diào)用)、call编丘、apply

  • 模擬bind實(shí)現(xiàn)
// 模擬bind實(shí)現(xiàn)
Function.prototype.myBind = function (context, ...args) {
    return (...rest) => this.call(context, ...args, ...rest)
}

執(zhí)行上下文

  • 全局執(zhí)行上下文

  • 函數(shù)級執(zhí)行上下文
    函數(shù)的執(zhí)行階段可以分為:
    1.函數(shù)建立階段:當(dāng)調(diào)用函數(shù)時(shí)与学,還沒有執(zhí)行函數(shù)內(nèi)部的代碼

    • variableObject(VO):收集函數(shù)中的arguments、參數(shù)嘉抓、內(nèi)部成員索守;
    • scopeChains:詞法環(huán)境,作用域鏈抑片,記錄當(dāng)前函數(shù)所在父級作用域中的活動(dòng)對象卵佛;[[Scopes]]作用域鏈,函數(shù)在創(chuàng)建時(shí)就會(huì)生成該屬性敞斋,js引擎才可以訪問截汪,這個(gè)屬性中存儲(chǔ)的是所有父級中的變量對象。
    • this:當(dāng)前函數(shù)內(nèi)部的this指向植捎,this的指向是動(dòng)態(tài)確定的衙解,當(dāng)函數(shù)在調(diào)用時(shí)才能確定。

    2.函數(shù)執(zhí)行階段

    • activationObject(AO):用AO指向VO
  • eval執(zhí)行上下文

閉包

能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)焰枢。

本質(zhì):函數(shù)在執(zhí)行時(shí)會(huì)被放入執(zhí)行棧蚓峦,當(dāng)函數(shù)執(zhí)行完畢會(huì)從執(zhí)行棧上移除,但堆上的作用域成員因?yàn)?strong>被外部引用不能釋放济锄,因此內(nèi)部函數(shù)依然可以訪問外部函數(shù)的成員暑椰。

作用:
1.可以讀取函數(shù)內(nèi)部的變量;
2.讓這些變量的值始終保持在內(nèi)存中荐绝。

使用注意點(diǎn):
1.由于閉包會(huì)使函數(shù)中的變量都被保存在內(nèi)存中一汽,因此濫用閉包會(huì)造成性能問題。解決方法:在退出函數(shù)之前低滩,刪除所有無用的變量召夹。
2.不要隨意修改父函數(shù)中變量的值。

// once
function once(fn) {
    let done = false
    return function () {
        if (!done) {
            done = true
            // arguments:類數(shù)組對象恕沫,存儲(chǔ)傳入函數(shù)的所有參數(shù)戳鹅。具有數(shù)組的部分屬性,比如.length昏兆、索引
            // apply(obj, args):
            // 劫持另一個(gè)對象的方法枫虏,繼承另一個(gè)對象的屬性妇穴,obj代替function中的this對象,args作為參數(shù)傳遞給函數(shù)args-->arguments
            return fn.apply(this, arguments)
        }
    }
}

純函數(shù)(??重點(diǎn)掌握)

相同的輸入永遠(yuǎn)會(huì)得到相同的輸出隶债,類似于數(shù)學(xué)中的函數(shù)關(guān)系腾它,沒有任何可觀察的副作用(副作用不可能完全禁止)。

純函數(shù)的好處
  • 可緩存
// 模擬memorize
function memorize(fn) {
    let cache = {}

    return function () {
        let arg_str = JSON.stringify(arguments)
        cache[arg_str] = cache[arg_str] || fn.apply(fn, arguments)
        return cache[arg_str]
    }
}
  • 可測試
  • 并行處理

柯里化(??重點(diǎn)掌握)

當(dāng)一個(gè)函數(shù)有多個(gè)參數(shù)時(shí)先傳遞一部分參數(shù)調(diào)用它(這部分參數(shù)以后永遠(yuǎn)不變)死讹,然后返回一個(gè)參數(shù)接收剩余的參數(shù)瞒滴,返回結(jié)果。

對函數(shù)進(jìn)行降維赞警,為函數(shù)組合作準(zhǔn)備妓忍。

lodash中的柯里化函數(shù)
// 模擬_.curry()的實(shí)現(xiàn)
function curry(fn) {
    // ...args-剩余參數(shù):將不定量的參數(shù)表示為一個(gè)數(shù)組
    // 剩余參數(shù)和arguments的區(qū)別:
    // 1.剩余參數(shù)只包含那些沒有對應(yīng)形參的實(shí)參,而arguments包含傳給函數(shù)的所有實(shí)參愧旦;
    // 2.arguments對象不是一個(gè)真正的數(shù)組世剖,而剩余參數(shù)是一個(gè)真正的Array實(shí)例;
    // 3.arguments還有一些剩余的屬性笤虫。
    return function curriedFn(...args) {
        // 判斷實(shí)參和形參的個(gè)數(shù)
        // function.length -> 函數(shù)形參的個(gè)數(shù)
        if (args.length < fn.length) {
            return curriedFn(...args.concat(Array.from(arguments)))
        }
        return fn(...args)  // args展開
    }
}

函數(shù)組合(??重點(diǎn)掌握)

如果一個(gè)函數(shù)要經(jīng)過多個(gè)函數(shù)處理才能得到最終值旁瘫,可以把中間過程的函數(shù)組合為一個(gè)函數(shù)。順序默認(rèn)從右向左執(zhí)行琼蚯。

// 模擬lodash中的flowRight
function compose(...fns) {
    return function (value) {
        return fns.reverse().reduce(function (acc, fn) {
            return fn(acc)
        }, value)  // value為初始值
    }
}

// 使用鉤子函數(shù)
const compose = (...fns) => value => fns.reverse().reduce((acc, fn) => fn(acc), value)
如何調(diào)試函數(shù)組合

可以編寫一個(gè)接收“當(dāng)前位置標(biāo)記”和value并返回value的柯里化函數(shù)酬凳,插入到函數(shù)組合中,用于追蹤哪一步出錯(cuò)遭庶。

// 'NEVER SAY DIE' --> 'never-say-die'
// 調(diào)試
const trace = _.curry((tag, v) => {
    console.log(tag, v)
    return v
})

const split = _.curry((sep, str) => _.split(str, sep))
const join = _.curry((sep, arr) => _.join(arr, sep))
const map = _.curry((fn, arr) => _.map(arr, fn))

// 錯(cuò)誤
const f = _.flowRight(join('-'), trace('map之后'), _.toLower, trace('map之前'), split(' '))
// 正確
const f = _.flowRight(join('-'), trace('map之后'), map(_.toLower), trace('map之前'), split(' '))

console.log('NEVER SAY DIE');
lodash/fp

提供了對函數(shù)式編程友好的方法宁仔,提供了不可變自動(dòng)柯里化函數(shù)優(yōu)先峦睡、數(shù)據(jù)滯后的方法翎苫。

Functor(函子)

函子就是一個(gè)裝有一個(gè)變量的容器,通過map方法維護(hù)這個(gè)變量赐俗。

函子在開發(fā)中的實(shí)際應(yīng)用場景:作用是空值副作用(IO)、異常處理(Either)弊知、異步任務(wù)(Task)阻逮。

// Functor 函子
// 函數(shù)式的編程不直接操作值,而是由函子完成
class Container{
    // of靜態(tài)方法秩彤,可以省略new關(guān)鍵字創(chuàng)建對象
    static of(value) {
        return new Container(value)
    }

    constructor(value) {
        this._value = value
    }

    // map方法叔扼,傳入處理value的函數(shù),返回一個(gè)包含新值的函子
    map(fn) {
        return Container.of(fn(this._value))
    }
}

MayBe函子

// MayBe 函子
// 處理空置異常
class MayBe {
    static of(value) {
        return new MayBe(value)
    }

    constructor(value) {
        this._value = value
    }

    map(fn) {
        return MayBe.isNothing() ? MayBe.of(null) : MayBe.of(fn(this._value))
    }

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

Either函子

// Either函子
// 傳入空值調(diào)用的函子
class Left {
    static of(value) {
        return new Left(value)
    }

    constructor(value) {
        this._value = value
    }

    map(fn) {
        return this
    }
}

// 正常情況下調(diào)用的函子
class Right {
    static of(value) {
        return new Right(value)
    }

    constructor(value) {
        this._value = value
    }

    map(fn) {
        return Right.of(fn(this._value))
    }
}

// Either用來處理異常
function parseJSON(json) {
    try {
        return Right.of(JSON.parse(json))
    } catch (e) {
        return Left.of({ error: e.message })
    }
}

IO函子

IO函子中的_value是一個(gè)函數(shù)漫雷,可以把不純的動(dòng)作存儲(chǔ)到_value中瓜富,延遲這個(gè)不純的操作。

const fp = require('lodash/fp')

// IO函子
class IO {
    static of(value) {
        // 給當(dāng)前傳入的value包裹一層函數(shù)降盹,讓不純的操作滯后發(fā)生与柑,保證當(dāng)前函數(shù)相同輸入得到相同輸出。
        // 將不純的操作交給調(diào)用者處理。
        return new IO(function () {
            return value
        })
    }

    constructor(fn) {
        this._value = fn
    }

    map(fn) {
        return new IO(fp.flowRight(fn, this._value))
    }
}

Task異步執(zhí)行

// folktale中的task函子价捧,處理異步執(zhí)行
function readFile(filename) {
    return task(resolver => {
        fs.readFile(filename, 'utf-8', (err, data) => {
            if (err) resolver.reject(err)
            resolver.resolve(data)
        })
    })
}

// 調(diào)用run執(zhí)行
readFile('package.json')
    .map(split('\n'))
    .map(find(x => x.includes('version')))
    .run()
    .listen({
        onRejected: err => {
            console.log(err)
        },
        onResolved: value => {
            console.log(value)
        }
    })

Pointed函子

是實(shí)現(xiàn)了of靜態(tài)方法的函子丑念,更深層的意義是將值放到上下文Context(把值放到容器中,使用map處理值)结蟋。

Monad(單子)

是可以變扁的Pointed函子脯倚,具有join和of兩個(gè)方法,能夠解決IO函子中出現(xiàn)的多層嵌套問題嵌屎。

const fp = require('lodash/fp')

// IO Monad
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))
    }

    // 返回調(diào)用后的值推正,減少一層嵌套
    join() {
        return this._value()
    }

    // 拍平map
    flatMap(fn) {
        return this.map(fn).join()
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市宝惰,隨后出現(xiàn)的幾起案子植榕,更是在濱河造成了極大的恐慌,老刑警劉巖掌测,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件内贮,死亡現(xiàn)場離奇詭異,居然都是意外死亡汞斧,警方通過查閱死者的電腦和手機(jī)夜郁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來粘勒,“玉大人竞端,你說我怎么就攤上這事∶硭” “怎么了事富?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長乘陪。 經(jīng)常有香客問我统台,道長,這世上最難降的妖魔是什么啡邑? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任贱勃,我火速辦了婚禮,結(jié)果婚禮上谤逼,老公的妹妹穿的比我還像新娘贵扰。我一直安慰自己,他們只是感情好流部,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布戚绕。 她就那樣靜靜地躺著,像睡著了一般枝冀。 火紅的嫁衣襯著肌膚如雪舞丛。 梳的紋絲不亂的頭發(fā)上耘子,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天,我揣著相機(jī)與錄音瓷马,去河邊找鬼拴还。 笑死,一個(gè)胖子當(dāng)著我的面吹牛欧聘,可吹牛的內(nèi)容都是我干的片林。 我是一名探鬼主播,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼怀骤,長吁一口氣:“原來是場噩夢啊……” “哼费封!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蒋伦,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤弓摘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后痕届,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體韧献,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年研叫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了锤窑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,716評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嚷炉,死狀恐怖渊啰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情申屹,我是刑警寧澤绘证,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站哗讥,受9級特大地震影響嚷那,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜杆煞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一魏宽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧索绪,春花似錦湖员、人聲如沸贫悄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽窄坦。三九已至唤反,卻和暖如春凳寺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背彤侍。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工肠缨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人盏阶。 一個(gè)月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓晒奕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親名斟。 傳聞我的和親對象是個(gè)殘疾皇子脑慧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容

  • 在前端快速發(fā)展的今天,如果不能時(shí)刻保持學(xué)習(xí)就會(huì)很快被淘汰砰盐。分享一下最近學(xué)習(xí)的函數(shù)式編程的相關(guān)知識闷袒,希望對大家有所幫...
    gongyexj閱讀 789評論 0 0
  • Study Notes[https://wuner.gitee.io/wuner-notes/fed-e-task...
    Wuner閱讀 525評論 0 0
  • 拉勾大前端的筆記囊骤,僅作為學(xué)習(xí)記錄 課程介紹 為什么學(xué)習(xí)函數(shù)式編程,以及什么是函數(shù)編程 函數(shù)式編程的特性(純函數(shù)冀值,柯...
    yapingXu閱讀 285評論 0 3
  • 函數(shù)式編程范式 為什么學(xué)習(xí)函數(shù)式編程 函數(shù)式編程是隨著react的流行受到了越來越多的關(guān)注 vue 3也開始擁抱函...
    _咻咻咻咻咻閱讀 193評論 0 0
  • 第一章: 函數(shù)式編程主要基于數(shù)學(xué)函數(shù)和它的思想也物。 1.1 函數(shù)與js方法: 函數(shù)是一段可以通過其名稱被調(diào)用的代碼,...
    yuhuan121閱讀 11,906評論 5 8