亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JS輕量級(jí)函數(shù)式編程實(shí)現(xiàn)XDM三

 更新時(shí)間:2022年06月14日 16:49:38   作者:掘金安東尼  
這篇文章主要為大家介紹了JS輕量級(jí)函數(shù)式編程實(shí)現(xiàn)XDM示例詳解第3/3篇,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

這是【JS如何函數(shù)式編程】系列文章第三篇。點(diǎn)贊??關(guān)注??,持續(xù)追蹤??

前兩篇傳送門:

《XDM,JS如何函數(shù)式編程?看這就夠了?。ㄒ唬?/a>

《XDM,JS如何函數(shù)式編程?看這就夠了?。ǘ?/a>

在第二篇,我們談了基礎(chǔ)之基礎(chǔ),重要之重要——“偏函數(shù)”,偏函數(shù)通過(guò)函數(shù)封裝,實(shí)現(xiàn)了減少傳參數(shù)量的目的,解決了手動(dòng)指定實(shí)參的麻煩。

更具重要意義的是:

當(dāng)函數(shù)只有一個(gè)形參時(shí),我們能夠比較容易地組合它們。這種單元函數(shù),便于進(jìn)行后續(xù)的組合函數(shù);

沒錯(cuò),本篇就是談關(guān)于 “組合函數(shù)”。它是函數(shù)編程的重中之重之重之重重重!

組合函數(shù)

含義

函數(shù)編程就像拼樂高!

樂高有各式各樣的零部件,我們將它們組裝拼接,拼成一個(gè)更大的組件或模型。

函數(shù)編程也有各種功能的函數(shù),我們將它們組裝拼接,用于實(shí)現(xiàn)某個(gè)特定的功能。

下面來(lái)看一個(gè)例子,比如我們要使用這兩個(gè)函數(shù)來(lái)分析文本字符串:

function words(str) {
    return String( str )
        .toLowerCase()
        .split( /\s|\b/ )
        .filter( function alpha(v){
            return /^[\w]+$/.test( v );
        } );
}
function unique(list) {
    var uniqList = [];
    for (let i = 0; i < list.length; i++) {
        if (uniqList.indexOf( list[i] ) === -1 ) {
            uniqList.push( list[i] );
        }
    }
    return uniqList;
}
var text = "To compose two functions together";
var wordsFound = words( text );
var wordsUsed = unique( wordsFound );
wordsUsed;
//  ["to", "compose", "two", "functions", "together"]

不用細(xì)看,只用知道:我們先用 words 函數(shù)處理了 text,然后用 unique 函數(shù)處理了上一處理的結(jié)果 wordsFound;

這樣的過(guò)程就好比生產(chǎn)線上加工商品,流水線加工。

想象一下,如果你是工廠老板,還會(huì)怎樣優(yōu)化流程、節(jié)約成本?

這里作者給了一種解決方式:去掉傳送帶!

即減少中間變量,我們可以這樣調(diào)用:

var wordsUsed = unique( words( text ) );
wordsUsed

確實(shí),少了中間變量,更加清晰,還能再優(yōu)化嗎?

我們還可以進(jìn)一步把整個(gè)處理流程封裝到一個(gè)函數(shù)內(nèi):

function uniqueWords(str) {
    return unique( words( str ) );
}
uniqueWords(text)

這樣就像是一個(gè)黑盒,無(wú)需管里面的流程,只用知道這個(gè)盒子輸入是什么!輸出是什么!輸入輸出清晰,功能清晰,非常“干凈”!如圖:

與此同時(shí),它還能被搬來(lái)搬去,或再繼續(xù)組裝。

我們回到 uniqueWords() 函數(shù)的內(nèi)部,它的數(shù)據(jù)流也是清晰的:

uniqueWords <-- unique <-- words <-- text

封裝盒子

上面的封裝 uniqueWords 盒子很 nice ,如果要不斷的封裝像 uniqueWords 的盒子,我們要一個(gè)一個(gè)的去寫嗎?

function uniqueWords(str) {
    return unique( words( str ) );
}
function uniqueWords_A(str) {
    return unique_A( words_A( str ) );
}
function uniqueWords_B(str) {
    return unique_B( words_B( str ) );
}
...

所以,一切為了偷懶,我們可以寫一個(gè)功能更加強(qiáng)大的函數(shù)來(lái)實(shí)現(xiàn)自動(dòng)封裝盒子:

function compose2(fn2,fn1) {
    return function composed(origValue){
        return fn2( fn1( origValue ) );
    };
}
// ES6 箭頭函數(shù)形式寫法
var compose2 =
    (fn2,fn1) =>
        origValue =>
            fn2( fn1( origValue ) );

接著,調(diào)用就變成了這樣:

var uniqueWords = compose2( unique, words );
var uniqueWords_A = compose2( unique_A, words_A );
var uniqueWords_B = compose2( unique_B, words_B );

太清晰了!

任意組合

上面,我們組合了兩個(gè)函數(shù),實(shí)際上我們也可以組合 N 個(gè)函數(shù);

finalValue <-- func1 <-- func2 <-- ... <-- funcN <-- origValue

比如用一個(gè) compose 函數(shù)來(lái)實(shí)現(xiàn)(敲重點(diǎn)):

function compose(...fns) {
    return function composed(result){
        // 拷貝一份保存函數(shù)的數(shù)組
        var list = fns.slice();
        while (list.length > 0) {
            // 將最后一個(gè)函數(shù)從列表尾部拿出
            // 并執(zhí)行它
            result = list.pop()( result );
        }
        return result;
    };
}
// ES6 箭頭函數(shù)形式寫法
var compose =
    (...fns) =>
        result => {
            var list = fns.slice();
            while (list.length > 0) {
                // 將最后一個(gè)函數(shù)從列表尾部拿出
                // 并執(zhí)行它
                result = list.pop()( result );
            }
            return result;
        };

基于前面 uniqueWords(..) 的例子,我們進(jìn)一步再增加一個(gè)函數(shù)來(lái)處理(過(guò)濾掉長(zhǎng)度小于等于4的字符串):

function skipShortWords(list) {
    var filteredList = [];
    for (let i = 0; i < list.length; i++) {
        if (list[i].length > 4) {
            filteredList.push( list[i] );
        }
    }
    return filteredList;
}
var text = "To compose two functions together";
var biggerWords = compose( skipShortWords, unique, words );
var wordsUsed = biggerWords( text );
wordsUsed;
// ["compose", "functions", "together"]

這樣 compose 函數(shù)就有三個(gè)入?yún)⑶叶际呛瘮?shù)了。我們還可以利用偏函數(shù)的特性實(shí)現(xiàn)更多:

function skipLongWords(list) { /* .. */ }
var filterWords = partialRight( compose, unique, words ); // 固定 unique 函數(shù) 和 words 函數(shù)
var biggerWords = filterWords( skipShortWords );
var shorterWords = filterWords( skipLongWords );
biggerWords( text );
shorterWords( text );

filterWords 函數(shù)是一個(gè)更具有特定功能的變體(根據(jù)第一個(gè)函數(shù)的功能來(lái)過(guò)濾字符串)。

compose 變體

compose(..)函數(shù)非常重要,但我們可能不會(huì)在生產(chǎn)中使用自己寫的 compose(..),而更傾向于使用某個(gè)庫(kù)所提供的方案。了解其底層工作的原理,對(duì)我們強(qiáng)化理解函數(shù)式編程也非常有用。

我們理解下 compose(..) 的另一種變體 —— 遞歸的方式實(shí)現(xiàn):

function compose(...fns) {
    // 拿出最后兩個(gè)參數(shù)
    var [ fn1, fn2, ...rest ] = fns.reverse();
    var composedFn = function composed(...args){
        return fn2( fn1( ...args ) );
    };
    if (rest.length == 0) return composedFn;
    return compose( ...rest.reverse(), composedFn );
}
// ES6 箭頭函數(shù)形式寫法
var compose =
    (...fns) => {
        // 拿出最后兩個(gè)參數(shù)
        var [ fn1, fn2, ...rest ] = fns.reverse();
        var composedFn =
            (...args) =>
                fn2( fn1( ...args ) );
        if (rest.length == 0) return composedFn;
        return compose( ...rest.reverse(), composedFn );
    };

通過(guò)遞歸進(jìn)行重復(fù)的動(dòng)作比在循環(huán)中跟蹤運(yùn)行結(jié)果更易懂,這可能需要更多時(shí)間去體會(huì);

基于之前的例子,如果我們想讓參數(shù)反轉(zhuǎn):

var biggerWords = compose( skipShortWords, unique, words );
// 變成
var biggerWords = pipe( words, unique, skipShortWords );

只需要更改 compose(..) 內(nèi)部實(shí)現(xiàn)這一句就行:

...
        while (list.length > 0) {
            // 從列表中取第一個(gè)函數(shù)并執(zhí)行
            result = list.shift()( result );
        }
...

雖然只是顛倒參數(shù)順序,這二者沒有本質(zhì)上的區(qū)別。

抽象能力

你是否會(huì)疑問(wèn):什么情況下可以封裝成上述的“盒子”呢?

這就很考驗(yàn) —— 抽象的能力了!

實(shí)際上,有兩個(gè)或多個(gè)任務(wù)存在公共部分,我們就可以進(jìn)行封裝了。

比如:

function saveComment(txt) {
    if (txt != "") {
        comments[comments.length] = txt;
    }
}
function trackEvent(evt) {
    if (evt.name !== undefined) {
        events[evt.name] = evt;
    }
}

就可以抽象封裝為:

function storeData(store,location,value) {
    store[location] = value;
}
function saveComment(txt) {
    if (txt != "") {
        storeData( comments, comments.length, txt );
    }
}
function trackEvent(evt) {
    if (evt.name !== undefined) {
        storeData( events, evt.name, evt );
    }
}

在做這類抽象時(shí),有一個(gè)原則是,通常被稱作 DRY(don't repeat yourself),即便我們要花時(shí)間做這些非必要的工作。

抽象能讓你的代碼走得更遠(yuǎn)! 比如上例,還能進(jìn)一步升級(jí):

function conditionallyStoreData(store,location,value,checkFn) {
    if (checkFn( value, store, location )) {
        store[location] = value;
    }
}
function notEmpty(val) { return val != ""; }
function isUndefined(val) { return val === undefined; }
function isPropUndefined(val,obj,prop) {
    return isUndefined( obj[prop] );
}
function saveComment(txt) {
    conditionallyStoreData( comments, comments.length, txt, notEmpty );
}
function trackEvent(evt) {
    conditionallyStoreData( events, evt.name, evt, isPropUndefined );
}

這樣 if 語(yǔ)句也被抽象封裝了。

抽象是一個(gè)過(guò)程,程序員將一個(gè)名字與潛在的復(fù)雜程序片段關(guān)聯(lián)起來(lái),這樣該名字就能夠被認(rèn)為代表函數(shù)的目的,而不是代表函數(shù)如何實(shí)現(xiàn)的。通過(guò)隱藏?zé)o關(guān)的細(xì)節(jié),抽象降低了概念復(fù)雜度,讓程序員在任意時(shí)間都可以集中注意力在程序內(nèi)容中的可維護(hù)子集上。—— 《程序設(shè)計(jì)語(yǔ)言》

我們?cè)诒鞠盗谐跏继岬剑?ldquo;一切為了創(chuàng)造更可讀、更易理解的代碼。”

從另一個(gè)角度,抽象就是將命令式代碼變成聲命式代碼的過(guò)程。從“怎么做”轉(zhuǎn)化成“是什么”。

命令式代碼主要關(guān)心的是描述怎么做來(lái)準(zhǔn)確完成一項(xiàng)任務(wù)。聲明式代碼則是描述輸出應(yīng)該是什么,并將具體實(shí)現(xiàn)交給其它部分。

比如 ES6 增加的結(jié)構(gòu)語(yǔ)法:

function getData() {
    return [1,2,3,4,5];
}
// 命令式
var tmp = getData();
var a = tmp[0];
var b = tmp[3];
// 聲明式
var [ a ,,, b ] = getData();

開發(fā)者需要對(duì)他們程序中每個(gè)部分使用恰當(dāng)?shù)某橄蠹?jí)別保持謹(jǐn)慎,不能太過(guò),也不能不夠。

階段小結(jié)

函數(shù)組合是為了符合“聲明式編程風(fēng)格”,即關(guān)注“是什么”,而非具體“做什么”。

它能將一個(gè)函數(shù)調(diào)用的輸出路由跳轉(zhuǎn)到另一個(gè)函數(shù)的調(diào)用上,然后一直進(jìn)行下去,它借助 compose(..) 或它的變體實(shí)現(xiàn)。。

我們期望組合中的函數(shù)是一元的(輸入輸出盡量是一個(gè)),這個(gè)也是前篇有提到的很重要的一個(gè)點(diǎn)。

組合 ———— 聲明式數(shù)據(jù)流 ———— 是支撐函數(shù)式編程其他特性的最重要的工具之一!

以上就是JS輕量級(jí)函數(shù)式編程實(shí)現(xiàn)XDM三的詳細(xì)內(nèi)容,更多關(guān)于JS輕量級(jí)函數(shù)式編程XDM的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 微信小程序 開發(fā)之快遞查詢功能的實(shí)現(xiàn)

    微信小程序 開發(fā)之快遞查詢功能的實(shí)現(xiàn)

    這篇文章主要介紹了微信小程序 開發(fā)之快遞查詢功能的實(shí)現(xiàn)的相關(guān)資料,這里實(shí)現(xiàn)微信小程序查詢快遞的功能,需要的朋友可以參考下
    2017-01-01
  • 微信小程序 登錄實(shí)例詳解

    微信小程序 登錄實(shí)例詳解

    這篇文章主要介紹了微信小程序 登錄實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下
    2017-01-01
  • 淺談克隆 JavaScript

    淺談克隆 JavaScript

    這篇文章主要介紹了克隆 JavaScript,克隆又有淺克隆與深克隆,文章圍繞JavaScript淺克隆與深克隆的相關(guān)資料展開具體內(nèi)容,需要的朋友可以參考一下
    2021-10-10
  • 微信小程序 簡(jiǎn)單教程實(shí)例詳解

    微信小程序 簡(jiǎn)單教程實(shí)例詳解

    這篇文章主要介紹了微信小程序 簡(jiǎn)單教程實(shí)例詳解的相關(guān)資料,這里對(duì)開發(fā)微信小程序步驟做了一一詳解,需要的朋友可以參考下
    2017-01-01
  • 手寫實(shí)現(xiàn)JS中的new

    手寫實(shí)現(xiàn)JS中的new

    這篇文章主要介紹JS中的new,new 運(yùn)算符創(chuàng)建一個(gè)用戶定義的對(duì)象類型的實(shí)例或具有構(gòu)造函數(shù)的內(nèi)置對(duì)象的實(shí)例。下面我們一起來(lái)看看我呢很臟具體內(nèi)容的詳細(xì)介紹,需要的朋友可以參考一下
    2021-11-11
  • JS?class語(yǔ)法糖的深入剖析

    JS?class語(yǔ)法糖的深入剖析

    這篇文章主要為大家介紹了JS?class語(yǔ)法糖的深入剖析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • JavaScript中isPrototypeOf函數(shù)

    JavaScript中isPrototypeOf函數(shù)

    這篇文章主要介紹了JavaScript中isPrototypeOf函數(shù),isPrototypeOf() 是 Object函數(shù)(類)的下的一個(gè)方法,用于判斷當(dāng)前對(duì)象是否為另外一個(gè)對(duì)象的原型,如果是就返回 true,否則就返回 false,下面來(lái)看看詳細(xì)內(nèi)容,需要的朋友可以參考一下
    2021-11-11
  • Dragonfly P2P 傳輸協(xié)議優(yōu)化代碼解析

    Dragonfly P2P 傳輸協(xié)議優(yōu)化代碼解析

    這篇文章主要為大家介紹了Dragonfly P2P 傳輸協(xié)議優(yōu)化代碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • 前端工程化cjs?umd?esm?打包差異詳解

    前端工程化cjs?umd?esm?打包差異詳解

    這篇文章主要為大家介紹了前端工程化cjs?umd?esm?打包差異詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • 微前端框架qiankun源碼剖析之上篇

    微前端框架qiankun源碼剖析之上篇

    這篇文章主要為大家介紹了微前端框架qiankun的源碼剖析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02

最新評(píng)論