亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

搜索

JavaScript函數(shù)式真正的淺析

原創(chuàng) 2016-11-10 16:42:17 410
摘要:最近兩年你要說函數(shù)式編程不火的話, 那是不可能的, 是人都知道函數(shù)式編程很火.為什么函數(shù)式編程會(huì)火呢, 在于它的思想, 很強(qiáng)大, 很強(qiáng)勢(shì)!尤其是前端的redux更是在reducer上完全使用純函數(shù), 函數(shù)式的好處漸漸被發(fā)掘出來, 筆者最近看了一些函數(shù)式方面的東東, 現(xiàn)在發(fā)出來給大家學(xué)習(xí)學(xué)習(xí), 順便我也學(xué)習(xí)學(xué)習(xí)怎么寫文章。JS函數(shù)式淺析1.入門的導(dǎo)語最近兩年你要說函數(shù)式編程不火的話, 那是不可能的,

最近兩年你要說函數(shù)式編程不火的話, 那是不可能的, 是人都知道函數(shù)式編程很火.為什么函數(shù)式編程會(huì)火呢, 在于它的思想, 很強(qiáng)大, 很強(qiáng)勢(shì)!尤其是前端的redux更是在reducer上完全使用純函數(shù), 函數(shù)式的好處漸漸被發(fā)掘出來, 筆者最近看了一些函數(shù)式方面的東東, 現(xiàn)在發(fā)出來給大家學(xué)習(xí)學(xué)習(xí), 順便我也學(xué)習(xí)學(xué)習(xí)怎么寫文章。

JS函數(shù)式淺析

1.入門的導(dǎo)語

最近兩年你要說函數(shù)式編程不火的話, 那是不可能的, 是人都知道函數(shù)式編程很火.為什么函數(shù)式編程會(huì)火呢, 在于它的思想, 很強(qiáng)大, 很強(qiáng)勢(shì)!尤其是前端的redux更是在reducer上完全使用純函數(shù), 函數(shù)式的好處漸漸被發(fā)掘出來, 筆者最近看了一些函數(shù)式方面的東東, 現(xiàn)在發(fā)出來給大家學(xué)習(xí)學(xué)習(xí), 順便我也學(xué)習(xí)學(xué)習(xí)怎么寫文章... :P

常用的函數(shù)式庫:

  • ramda 設(shè)計(jì)很棒的一個(gè)庫

  • lodash 比較常用的一個(gè)庫

  • underscore 應(yīng)該也不錯(cuò)的一個(gè)庫

2.純函數(shù)

定義: 相同輸入一定得到相同輸出且運(yùn)行過程中不修改,不讀取外部環(huán)境的變量的函數(shù)

說出來肯定不好理解, 還是要看看代碼. 就好像你不看國足比賽永遠(yuǎn)不知道國足為什么會(huì)輸給月薪幾百塊的敘利亞.

// Array.slice 對(duì)于固定輸入一定是固定輸出, 且不依賴外部變量, 啥? 依賴了arr變量嗎? 
// 其實(shí)這種寫法和Array.prototype.slice(arr, 0, 3); 是一樣的. 這樣就理解了, 
// 你還學(xué)到一個(gè)東西 Array.slice是不會(huì)修改原數(shù)組滴! 
var arr = [1,2,3,4,5]; 
arr.slice(0,3); 
 
 // Array.splice 會(huì)修改xs, 所以是不純的, 所以相同的輸入不會(huì)有相同的輸出! 
var xs.splice(0,3); 
//=> [1,2,3] 
xs.splice(0,3); 
//=> [4,5] 
xs.splice(0,3); 
//=> []

純函數(shù)的好處: 不會(huì)去修改外部變量就不會(huì)產(chǎn)生線程安全問題.可以極大的減少系統(tǒng)復(fù)雜程度

3.函數(shù)的柯里化

看! 代碼!

// 調(diào)用 doWht('我', '家里', '飯'); 
let doWhat = (who, where, what) => { 
  return who + '在' + where + '做' + what 
} 
 
// 柯里化后的等價(jià)效果 
// 調(diào)用 doWhat('我')('家里')('飯') 
let doWhat = who => where => what => { 
  return who + '在' + where + '做' + what 
} 
 
// 假設(shè)現(xiàn)在知道是'我'在'家', 至于做什么是不知道的 
// tmp函數(shù)就已經(jīng)幫我們保存了值, 這樣是非常靈活的. 
let doWhatCurry = doWhat('我')('家里')

上面提到的庫里都有一個(gè)叫curry的函數(shù)會(huì)將一個(gè)普通的函數(shù)柯里化.

4.函數(shù)的組合

函數(shù)組合是將函數(shù)組合在一起, 生成一個(gè)新的函數(shù)

// h(g(f(x))) 這是以前調(diào)用函數(shù)的方式 
var add1 = x => x + 1 
var mul5 = x => x * 5 
// compose會(huì)生成一個(gè)新的函數(shù), 接收的參數(shù)全部傳給add1, 然后add1的返回值傳給mul5(注意注意!, mul5的參數(shù)個(gè)數(shù)只能有一個(gè)!!!), 然后compose生成的新的函數(shù)的返回值就是mul5的返回值. 
compose(mul5, add1)(2)

函數(shù)組合非常強(qiáng)大, 能夠通過組合的方式來生成新的函數(shù), 這是非常爽的. 如果你運(yùn)用靈活, 會(huì)極大的減少你的代碼量(如果不能減少別噴我啊), compose的實(shí)現(xiàn)在上面提到的三個(gè)庫中都有實(shí)現(xiàn).

5.聲明式與命令式風(fēng)格

命令式的風(fēng)格讓我們通過代碼引導(dǎo)機(jī)器, 讓機(jī)器一步一步完成我們要的任務(wù); 而聲明式則是直接告訴機(jī)器我要做啥, 更直觀.

//命令式 var persons = [...] for (var i = 0; persons.length; ++i) {   persons[i] = persons[i].toUppercase() }  //聲明式 var persons = [...] persons.map(person => person.toUppercase())  

6.Point free風(fēng)格

//命令式 
var persons = [...] 
for (var i = 0; persons.length; ++i) { 
  persons[i] = persons[i].toUppercase() 
} 
 
//聲明式 
var persons = [...] 
persons.map(person => person.toUppercase())

現(xiàn)在是推薦使用point free風(fēng)格的代碼(定義函數(shù)時(shí)), 這會(huì)減少我們不必要的命名. 多用這種風(fēng)格哦!

7.容器(Functor)

容器代表了一個(gè)值, 一個(gè)任意值. 他就好像是函數(shù)式編程里的變量,函數(shù)的一個(gè)鎧甲.可以讓你的變量,函數(shù)在工程的戰(zhàn)場(chǎng)中所向披靡!

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用來構(gòu)建容器, map用來變換容器 
// Functor可以做很多很多事情, 具體的? 往下介紹.
// Maybe就是在普通容器上新增了一個(gè)檢查空值的行為.  
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是空的話就會(huì)輸出空了 
var functor = Maybe.of({name: ‘mrcode'}) 
functor 
    .map(value => value.age) 
    .map(String.prototype.upperCase) 
    .map(value => console.log(value))

這個(gè)Maybe到底有啥用呢? 就是空值檢測(cè), 看上面的例子, 如果不進(jìn)行判空的話, 第二個(gè)map就會(huì)調(diào)用String.prototype.upperCase函數(shù), 會(huì)拋出異常的, 怕了吧? :P, 而且, 現(xiàn)在很多語言,swift等都添加了類似的支持. optional

Maybe只能判空, 但是Either才是真正的處理錯(cuò)誤的容器, Either有兩個(gè)子類, Left和Right.

// Promise是通過catch方法來接收錯(cuò)誤的 如: 
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)); 
} 
 
// 應(yīng)用: 
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會(huì)跳過所有執(zhí)行過程, 直達(dá)結(jié)果, 這就好像Right是流程圖里一個(gè)又一個(gè)指向下一個(gè)任務(wù)的箭頭, 而Left直接指向了結(jié)果, 是錯(cuò)誤的結(jié)果.

8.IO

誒, 函數(shù)式編程里, 涉及到IO總是讓人尷尬的, 藍(lán)瘦的很..幸好, 有一種叫做IO的東西專門處理IO這種東西(別嫌繞哈), 看代碼,

var IO = function(f) { 
    this.__value = f; 
} 
 
// ??? 看不懂, 待會(huì)解釋.. 
IO.of = x => new IO(_ => x); 
 
// ??? 這是啥子鬼???? 
IO.prototype.map = function(f) { 
    return new IO(compose(f, this.__value)) 
};

權(quán)威解答: 這里的IO里存的是一個(gè)函數(shù), 包裹了外部環(huán)境變量的函數(shù), 我們傳入了一個(gè)函數(shù), 這個(gè)函數(shù)里包含了實(shí)際的值,會(huì)進(jìn)行IO操作. 我們把不純的IO操作放到了這個(gè)函數(shù)里, 總體上看, 我們的IO對(duì)象, 是不會(huì)執(zhí)行這些不純的操作的. 它依然是純的, 因?yàn)镮O操作壓根就沒執(zhí)行內(nèi)部包含的函數(shù), 這個(gè)函數(shù)是外部調(diào)用者去執(zhí)行的. 也就是說, 不純的操作是外部的人干的, 和我們的IO對(duì)象一丟丟關(guān)系都木有!(干得漂亮!) 看一個(gè)例子.

var io_document = new IO(_ => window.document); 
io_document.map(function(doc){ return doc.title }); 
// 得到IO(documen.title)

科普: 這里你沒有得到document.title, 你得到的僅僅是一個(gè)會(huì)返回document.title的一個(gè)函數(shù), 這個(gè)函數(shù)是不純的, 但是執(zhí)行不是由上面的代碼執(zhí)行的, 鍋在調(diào)用函數(shù)的人身上! 上面的代碼依然是'純'的!

9.Monad

看這個(gè)部分的時(shí)候建議看一下IO的實(shí)現(xiàn), 好好理解一下, 我知道有點(diǎn)燒腦, 但是看一下沒壞處!玩過Promise的都知道, Promise.then傳進(jìn)去的函數(shù)可以返回一個(gè)新的Promise. Promise就是Monad.

10.函數(shù)式編程的應(yīng)用

react中的純組件

// 固定的輸入得到固定的輸出 純組件極大的增加了react的靈活程度 
// app 的狀態(tài)交給一些狀態(tài)機(jī)管理 比如redux 
var Text = props => ( 
    <div style={props.style}>{props.text}</div> 
)

redux中的reducer

// 輸入當(dāng)前狀態(tài)和action, 輸出nowState 
reducer(currentState, action) => newState

11.總結(jié)一下

確實(shí)是這樣, 不總結(jié)的話就不像是一篇文章了, 還是總結(jié)下吧:

  • 純函數(shù)的概念以及函數(shù)柯里化和函數(shù)的組合

  • 容器概念, Container和Maybe, Either的派生Left,Right, IO作用.

  • 函數(shù)式編程的應(yīng)用


發(fā)布手記

熱門詞條