摘要:入門的導語廢話最近兩年你要說函數式編程不火的話那是不可能的是人都知道函數式編程很火為什么函數式編程會火呢在于它的思想很強大很強勢尤其是前端的更是在上完全使用純函數函數式的好處漸漸被發掘出來筆者最近看了一些函數式方面的東東現在發出來給大家學習
0x00 入門的導語(廢話)
最近兩年你要說函數式編程不火的話, 那是不可能的, 是人都知道函數式編程很火.為什么函數式編程會火呢, 在于它的思想, 很強大, 很強勢!尤其是前端的redux更是在reducer上完全使用純函數, 函數式的好處漸漸被發掘出來, 筆者最近看了一些函數式方面的東東, 現在發出來給大家學習學習, 順便我也學習學習怎么寫文章... :P
常用的函數式庫:
ramda 設計很棒的一個庫
lodash 比較常用的一個庫
underscore 應該也不錯的一個庫
0x01 純函數定義: 相同輸入一定得到相同輸出且運行過程中不修改,不讀取外部環境的變量的函數
說出來肯定不好理解, 還是要看看代碼. 就好像你不看國足比賽永遠不知道國足為什么會輸給月薪幾百塊的敘利亞.
// Array.slice 對于固定輸入一定是固定輸出, 且不依賴外部變量, 啥? 依賴了arr變量嗎? // 其實這種寫法和Array.prototype.slice(arr, 0, 3); 是一樣的. 這樣就理解了, // 你還學到一個東西 Array.slice是不會修改原數組滴! var arr = [1,2,3,4,5]; arr.slice(0,3); // Array.splice 會修改xs, 所以是不純的, 所以相同的輸入不會有相同的輸出! var xs.splice(0,3); //=> [1,2,3] xs.splice(0,3); //=> [4,5] xs.splice(0,3); //=> []
純函數的好處: 不會去修改外部變量就不會產生線程安全問題.可以極大的減少系統復雜程度
0x02 函數的柯里化看! 代碼!
// 調用 doWht("我", "家里", "飯"); let doWhat = (who, where, what) => { return who + "在" + where + "做" + what } // 柯里化后的等價效果 // 調用 doWhat("我")("家里")("飯") let doWhat = who => where => what => { return who + "在" + where + "做" + what } // 假設現在知道是"我"在"家", 至于做什么是不知道的 // tmp函數就已經幫我們保存了值, 這樣是非常靈活的. let doWhatCurry = doWhat("我")("家里")
上面提到的庫里都有一個叫curry的函數會將一個普通的函數柯里化.
0x03 函數的組合函數組合是將函數組合在一起, 生成一個新的函數
// h(g(f(x))) 這是以前調用函數的方式 var add1 = x => x + 1 var mul5 = x => x * 5 // compose會生成一個新的函數, 接收的參數全部傳給add1, 然后add1的返回值傳給mul5(注意注意!, mul5的參數個數只能有一個!!!), 然后compose生成的新的函數的返回值就是mul5的返回值. compose(mul5, add1)(2)
函數組合非常強大, 能夠通過組合的方式來生成新的函數, 這是非常爽的. 如果你運用靈活, 會極大的減少你的代碼量(如果不能減少別噴我啊), compose的實現在上面提到的三個庫中都有實現.
0x04 聲明式與命令式風格命令式的風格讓我們通過代碼引導機器, 讓機器一步一步完成我們要的任務; 而聲明式則是直接告訴機器我要做啥, 更直觀.
//命令式 var persons = [...] for (var i = 0; persons.length; ++i) { persons[i] = persons[i].toUppercase() } //聲明式 var persons = [...] persons.map(person => person.toUppercase())0x05 Point free風格
// 假定如果 let map = fn => list => list.map(fn); let add = (a, b) => a + b; // 函數incrementAll不是point free 風格 // 因為這里提到了numbers參數, 需要給出一個命名. // 這樣定義函數會導致我們需要多命名一個變量. 麻煩! let incrementAll = (numbers) => map(add(1))(numbers); // Point free風格的定義方法 // 假設add被柯里化過了 let incrementAll = map(add(1))
現在是推薦使用point free風格的代碼(定義函數時), 這會減少我們不必要的命名. 多用這種風格哦!
0x06 容器(Functor)容器代表了一個值, 一個任意值. 他就好像是函數式編程里的變量,函數的一個鎧甲.可以讓你的變量,函數在工程的戰場中所向披靡!
var Container = function(x) { this.__value = x; } Container.of = x => new Container(x); Container.prototype.map = function(f){ return Container.of(f(this.__value)) } Container.of(3).map(x => x+1).map(x => x*5) // of用來構建容器, map用來變換容器 // Functor可以做很多很多事情, 具體的? 往下介紹.
// Maybe就是在普通容器上新增了一個檢查空值的行為. var Maybe = function(x) { this.__value = x; } Maybe.of = function(x) { return new Maybe(x); } Maybe.prototype.map = function(f) { return this.isNothing() ? Maybe.of(null) : Maybe.of(f(this.__value)); } Maybe.prototype.isNothing = function() { return (this.__value === null || this.__value === undefined); } // 例子, 如果name是空的話就會輸出空了 var functor = Maybe.of({name: ‘mrcode"}) functor .map(value => value.age) .map(String.prototype.upperCase) .map(value => console.log(value))
這個Maybe到底有啥用呢? 就是空值檢測, 看上面的例子, 如果不進行判空的話, 第二個map就會調用String.prototype.upperCase函數, 會拋出異常的, 怕了吧? :P, 而且, 現在很多語言,swift等都添加了類似的支持. optional
Maybe只能判空, 但是Either才是真正的處理錯誤的容器, Either有兩個子類, Left和Right.
// Promise是通過catch方法來接收錯誤的 如: doSomething() .then(async1) .then(async2) .catch(e => console.log(e)); // 完全一樣 var Left = function(x) { this.__value = x; } var Right = function(x) { this.__value = x; } // 完全一樣 Left.of = function(x) { return new Left(x); } Right.of = function(x) { return new Right(x); } // 這里不同!!! Left.prototype.map = function(f) { return this; } Right.prototype.map = function(f) { return Right.of(f(this.__value)); } // 應用: var getAge = user => user.age ? Right.of(user.age) : Left.of("ERROR!") getAge({name: "stark", age: "21"}).map(age => "Age is " + age); //=> Right("Age is 21") getAge({name: "stark"}).map(age => "Age is " + age); //=> Left("ERROR!")
Left會跳過所有執行過程, 直達結果, 這就好像Right是流程圖里一個又一個指向下一個任務的箭頭, 而Left直接指向了結果, 是錯誤的結果.
0x07 IO誒, 函數式編程里, 涉及到IO總是讓人尷尬的, 藍瘦的很..幸好, 有一種叫做IO的東西專門處理IO這種東西(別嫌繞哈), 看代碼,
// 沒毛病 var IO = function(f) { this.__value = f; } // ??? 看不懂, 待會解釋.. IO.of = x => new IO(_ => x); // ??? 這是啥子鬼???? IO.prototype.map = function(f) { return new IO(compose(f, this.__value)) };
權威解答: 這里的IO里存的是一個函數, 包裹了外部環境變量的函數, 我們傳入了一個函數, 這個函數里包含了實際的值,會進行IO操作. 我們把不純的IO操作放到了這個函數里, 總體上看, 我們的IO對象, 是不會執行這些不純的操作的. 它依然是純的, 因為IO操作壓根就沒執行內部包含的函數, 這個函數是外部調用者去執行的. 也就是說, 不純的操作是外部的人干的, 和我們的IO對象一丟丟關系都木有!(干得漂亮!) 看一個例子.
var io_document = new IO(_ => window.document); io_document.map(function(doc){ return doc.title }); // 得到IO(documen.title)
科普: 這里你沒有得到document.title, 你得到的僅僅是一個會返回document.title的一個函數, 這個函數是不純的, 但是執行不是由上面的代碼執行的, 鍋在調用函數的人身上! 上面的代碼依然是"純"的!
0x08 Monad看這個部分的時候建議看一下IO的實現, 好好理解一下, 我知道有點燒腦, 但是看一下沒壞處!玩過Promise的都知道, Promise.then傳進去的函數可以返回一個新的Promise. Promise就是Monad.
0x09 函數式編程的應用 react中的純組件// 固定的輸入得到固定的輸出 純組件極大的增加了react的靈活程度 // app 的狀態交給一些狀態機管理 比如redux var Text = props => (redux中的reducer{props.text})
// 輸入當前狀態和action, 輸出nowState reducer(currentState, action) => newState0x10 總結一下
確實是這樣, 不總結的話就不像是一篇文章了, 還是總結下吧:
純函數的概念以及函數柯里化和函數的組合
容器概念, Container和Maybe, Either的派生Left,Right, IO作用.
函數式編程的應用
參考文章JavaScript函數式編程3
好了, 再見 古德曼~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/87970.html
摘要:響應式原理為了探究這一切的原因,我再次點開了的官網。在官網很下面的位置,找到了關于響應式原理的說明。因此,新添加到數組中的對象中的屬性,就成了非響應式的屬性了,改變它自然不會讓組件重新渲染。響應式屬性的對象,有這個對象就代表是響應式的。 ??最近在用Vue開發一個后臺管理的demo,有一個非常常規的需求。然而這個常規的需求中,包含了大量的知識點。有一個產品表格,用來顯示不同產品的信息。...
摘要:函數式編程,一看這個詞,簡直就是學院派的典范。所以這期周刊,我們就重點引入的函數式編程,淺入淺出,一窺函數式編程的思想,可能讓你對編程語言的理解更加融會貫通一些。但從根本上來說,函數式編程就是關于如使用通用的可復用函數進行組合編程。 showImg(https://segmentfault.com/img/bVGQuc); 函數式編程(Functional Programming),一...
摘要:綜上所述有原型鏈繼承,構造函數繼承經典繼承,組合繼承,寄生繼承,寄生組合繼承五種方法,寄生組合式繼承,集寄生式繼承和組合繼承的優點于一身是實現基于類型繼承的最有效方法。 一、前言 繼承是面向對象(OOP)語言中的一個最為人津津樂道的概念。許多面對對象(OOP)語言都支持兩種繼承方式::接口繼承 和 實現繼承 。 接口繼承只繼承方法簽名,而實現繼承則繼承實際的方法。由于js中方法沒有簽名...
閱讀 3768·2021-08-30 09:47
閱讀 3690·2019-08-30 15:56
閱讀 677·2019-08-30 14:18
閱讀 698·2019-08-29 16:17
閱讀 2065·2019-08-29 11:07
閱讀 642·2019-08-26 13:53
閱讀 3443·2019-08-26 10:26
閱讀 2491·2019-08-23 18:30