前端課程學(xué)習(xí)心得體會(huì)+學(xué)習(xí)筆記

      網(wǎng)友投稿 777 2022-05-30

      心得體會(huì)

      已經(jīng)學(xué)習(xí)了兩周半的大前端課程了,課程質(zhì)量真的是好得沒話說,我看過很多前端的課程,但從沒有哪家課程能將前端的知識(shí)體系劃分的如此全面細(xì)致,還能保證每一個(gè)知識(shí)點(diǎn)還都能講得如此透徹,在講知識(shí)點(diǎn)的基礎(chǔ)上還能開篇幅去講思想,更是難得。比如下面的函數(shù)式編程,這種編程范式我之前從來都沒使用過,更不知道柯里化、函數(shù)組合為何物。直到在拉鉤大前端課程中,每一個(gè)知識(shí)點(diǎn)的學(xué)習(xí),都讓我有種重獲新生的感覺,仿佛以前學(xué)習(xí)的東西都白學(xué)了,只知道簡(jiǎn)單的用法,不了解核心原理,更不會(huì)用高級(jí)特性。現(xiàn)在每學(xué)習(xí)完一個(gè)模塊,就期待著解鎖下一個(gè)模塊,迫不及待地想去知道下一個(gè)模塊可以讓自己get到哪些技能。

      課程的主講老師,汪磊老師,我看過他的webpack專欄,那時(shí)我就非常佩服他能夠把webpack這樣一個(gè)大而繁瑣的工具,講得如此細(xì)微易懂,讓我懂了webpack的插件機(jī)制和loader機(jī)制。在大前端課程中,汪磊老師更是讓我敬佩,我感覺他的知識(shí)面非常廣,說他什么都懂也不為過,他還總是把我們?cè)趯W(xué)習(xí)中會(huì)遇到的問題演示出來,或者是提出來讓我們注意。最感謝汪磊老師的地方,就是在JS異步章節(jié),直播課中的補(bǔ)充中,老師演示了各種function會(huì)影響this問題,道出了this取決于調(diào)用而不是定義,讓我醍醐灌頂,也徹底搞懂了JS的this的取值,那天晚上令我激動(dòng)地睡不著覺。

      除此之外,兩位助教老師還整天在群內(nèi)答疑,只要遇到不懂的地方,就可以立馬去群里問助教老師,老師會(huì)看到問題就會(huì)立馬回復(fù),如果是代碼執(zhí)行問題,還會(huì)把你的代碼下載下來親自運(yùn)行排查,真的是太貼心了。班主任老師會(huì)在群里每天督促同學(xué)們交作業(yè),遇到軟件問題、作業(yè)提交問題、聽課問題都可以找班主任老師。

      一個(gè)人學(xué)習(xí)或許會(huì)太孤獨(dú),但是在拉鉤教育大前端課程里,每天和幾百人一起學(xué)習(xí),群里還有專業(yè)的助教老師答疑,其他同學(xué)很多都是前端大佬,在你遇到問題的時(shí)候,無論是什么問題,只要是前端問題,總會(huì)有人給你解答或者提供思路。

      在拉鉤教育大前端課程中,一起學(xué)習(xí),使彼此共同成長(zhǎng)。

      函數(shù)式編程范式

      一、高階函數(shù)

      使用高階函數(shù)的意義:抽象可以幫我們屏蔽細(xì)節(jié),只需要關(guān)注于我們的目標(biāo)。高階函數(shù)是用來抽象通用的問題。

      function forEach (array, fn) { for (let i = 0; i < array.length; i++) { fn(array[i]) } } function filter (array, fn) { const res = [] for (let i = 0; i < array.length; i++) { if(fn(array[i])) { res.push(array[i]) } } return res } const arr = [1, 2, 4, 5, 2] forEach(arr, console.log) console.log(filter(arr, function (item) { return item % 2 === 0 }))

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

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

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      應(yīng)用:once函數(shù) 只執(zhí)行一次的函數(shù),比如說支付情況,無論用戶點(diǎn)多少次,這個(gè)函數(shù)都只執(zhí)行一次

      function once(fn) { let done = false return function () { if(!done) { done = true fn.apply(this, arguments) } } } let pay = once(function (money) { console.log(`支付了${money}元`) }) pay(1) // 支付了1元 pay(2) pay(3)

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

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

      forEach/map/filter/every/some/find/findIndex/reduce/sort

      // 模擬常用的高階函數(shù):map every some const arr = [1, 2, 3, 4] // map const map = (arr, fn) => { let result = [] for(let item of arr) { result.push(fn(item)) } return result } console.log(map(arr, val => val * val)) // [ 1, 4, 9, 16 ] // every const every = (arr, fn) => { for(let item of arr) { if(!fn(item))return false } return true } console.log(every(arr, v => v > 0)) // true // some const some = (arr, fn) => { for(let item of arr) { if(fn(item))return true } return false } console.log(some(arr, v => v % 2 == 0)) // true

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      二、閉包

      函數(shù)和其周圍的狀態(tài)(詞法環(huán)境)的引用捆綁在一起形成閉包。可以在一個(gè)作用域中調(diào)用一個(gè)函數(shù)的內(nèi)部函數(shù)并訪問到該函數(shù)的作用域中的成員。

      大前端課程學(xué)習(xí)心得體會(huì)+學(xué)習(xí)筆記

      本質(zhì):函數(shù)在執(zhí)行的時(shí)候會(huì)放到一個(gè)執(zhí)行棧上,當(dāng)函數(shù)執(zhí)行完畢之后會(huì)從執(zhí)行棧移除,但是堆上的作用域成員因?yàn)橥獠恳貌荒茚尫牛虼藘?nèi)部函數(shù)依然可以訪問外部函數(shù)的成員

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

      1

      2

      3

      4

      5

      6

      7

      8

      9

      閉包的應(yīng)用:

      function makePower(power) { return function (num) { return Math.pow(num, power) } } // 求平方 let power2 = makePower(2) let power3 = makePower(3) console.log(power2(4)) console.log(power2(5)) console.log(power3(4))

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      三、純函數(shù)

      相同的輸入永遠(yuǎn)會(huì)得到相同的輸出

      沒有任何可觀察的副作用

      類似數(shù)學(xué)中的函數(shù)

      lodash是一個(gè)純函數(shù)的功能庫(kù),提供了對(duì)數(shù)組、數(shù)字、對(duì)象、字符串、函數(shù)等操作的一些方法

      數(shù)組的slice和splice分別是純函數(shù)和不純的函數(shù)

      slice返回?cái)?shù)組中的指定部分,不改變?cè)瓟?shù)組

      splice對(duì)數(shù)組進(jìn)行操作返回該數(shù)組,會(huì)改變?cè)瓟?shù)組

      // 純函數(shù)slice和不純函數(shù)splice let arr = [1, 2, 3, 4, 5] console.log(arr.slice(0, 3)) console.log(arr.slice(0, 3)) console.log(arr.slice(0, 3)) console.log(arr.splice(0, 3)) console.log(arr.splice(0, 3)) console.log(arr.splice(0, 3)) function getSum(n1, n2) { return n1 + n2 } console.log(1, 2) console.log(1, 2) console.log(1, 2)

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      函數(shù)式編程不會(huì)保留計(jì)算中間的結(jié)果,所以變量是不可變的(無狀態(tài)的)

      我們可以把一個(gè)函數(shù)的執(zhí)行結(jié)果交給另一個(gè)函數(shù)去處理

      // 演示 lodash // first last toUpper reverse each includes find findIndx const _ = require('lodash') const arr = ['jal', 'cathy', 'yibo', 'lucy'] console.log(_.first(arr)) console.log(_.last(arr)) console.log(_.toUpper(_.first(arr))) console.log(_.reverse(arr)) const r = _.each(arr, (item, index) => { console.log(item, index) }) console.log(r)

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      緩存純函數(shù)結(jié)果案例:

      // 記憶函數(shù) const _ = require('lodash') function getArea (r) { console.log(r) return Math.PI * r * r * r } // let getAreaWithMemory = _.memoize(getArea) // console.log(getAreaWithMemory(4)) // console.log(getAreaWithMemory(4)) // console.log(getAreaWithMemory(4)) // console.log(getAreaWithMemory(4)) // 模擬memoize的實(shí)現(xiàn) function memoize(fn) { const cache = {} return function () { let key = JSON.stringify(arguments) cache[key] = cache[key] || fn.apply(fn, arguments) return cache[key] } } let getAreaWithMemory = memoize(getArea) console.log(getAreaWithMemory(4)) console.log(getAreaWithMemory(4)) console.log(getAreaWithMemory(4)) console.log(getAreaWithMemory(4))

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      可緩存

      因?yàn)榧兒瘮?shù)對(duì)相同的輸入始終有相同的結(jié)果,所以可以把純函數(shù)的結(jié)果緩存起來

      可測(cè)試

      純函數(shù)讓測(cè)試更加方便

      并行處理

      在多線程環(huán)境并行操作共享的內(nèi)存數(shù)據(jù)可能會(huì)出現(xiàn)意外情況

      純函數(shù)不需要訪問共享的內(nèi)存數(shù)據(jù),所以在并行環(huán)境下可以任意運(yùn)行純函數(shù)(Web Worker)

      純函數(shù)對(duì)于相同的輸入永遠(yuǎn)會(huì)得到相同的輸出,而且沒有任何可觀察的副作用

      // 不純的,函數(shù)的返回值依賴外部的變量 let mini = 18 function checkAge (age) { return age >= mini } // 純的(有硬編碼,后續(xù)可以通過柯里化解決) function checkAge2 (age) { let mini = 18 return age >= mini }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      副作用讓一個(gè)函數(shù)變得不純(如上例的checkAge中的mini是全局的),純函數(shù)的根據(jù)相同的輸入返回相同的輸出,如果函數(shù)依賴于外部的狀態(tài)就無法保證輸出相同,就會(huì)帶來副作用。

      副作用的來源:

      配置文件

      數(shù)據(jù)庫(kù)

      獲取用戶的輸入

      所有的外部交互都有可能代理副作用,副作用也是的方法通用性下降不適合擴(kuò)展和可重用性,同時(shí)副作用會(huì)給程序中帶來安全隱患給程序員帶來不確定性,但是副作用不可能完全禁止,盡可能控制他們?cè)诳煽胤秶鷥?nèi)發(fā)生。

      四、柯里化

      當(dāng)一個(gè)函數(shù)有多個(gè)參數(shù)的時(shí)候,先傳遞一部分參數(shù)調(diào)用它(這部分參數(shù)以后永遠(yuǎn)不變)

      然后返回一個(gè)新的函數(shù)接受剩余的參數(shù),返回結(jié)果

      // 柯里化演示 // function checkAge (age) { // let mini = 18 // return age >= mini // } // 普通的純函數(shù) function checkAge (mini, age) { return age >= mini } console.log(checkAge(18, 20)) console.log(checkAge(18, 24)) console.log(checkAge(22, 24)) // 閉包,高階函數(shù),函數(shù)的柯里化 function saveMini (mini) { return function (age) { return age >= mini } } // ES6 寫法, 同上 // const saveMini = mini => age => age >= mini const checkAge18 = saveMini(18) const checkAge22 = saveMini(22) console.log(checkAge18(20)) console.log(checkAge18(24)) console.log(checkAge22(24))

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      _.curry(func)

      功能:創(chuàng)建一個(gè)函數(shù),該函數(shù)接受一個(gè)或多個(gè)func的參數(shù),如果func所需要的參數(shù)都被提供則執(zhí)行func并返回執(zhí)行的結(jié)果,否則繼續(xù)返回改函數(shù)并等待接受剩余的參數(shù)。

      參數(shù):需要柯里化的函數(shù)

      返回值:柯里化后的函數(shù)

      // _.curry(func) const _ = require('lodash') function getSum (a, b, c) { return a + b + c } const curried = _.curry(getSum) console.log(curried(1, 2, 3)) // 6 console.log(curried(1)(2, 3))// 6 console.log(curried(1, 2)(3))// 6

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      案例:

      // 柯里化案例 // ''.match(/\s+/g) // ''.match(/\d+/g) const _ = require('lodash') const match = _.curry(function (reg, str) { return str.match(reg) }) const haveSpace = match(/\s+/g) console.log(haveSpace('hello world')) // [ ' ' ] console.log(haveSpace('hello')) // null const haveNumber = match(/\d+/g) console.log(haveNumber('123abc456def789')) // [ '123', '456', '789' ] console.log(haveNumber('jal')) // null const filter = _.curry(function (func, arr) { return arr.filter(func) }) console.log(filter(haveSpace, ['hello world', 'Ji Ailing', 'cathy', 'yibo', 'Wang Yibo'])) // [ 'hello world', 'Ji Ailing', 'Wang Yibo' ] const findSpace = filter(haveSpace) console.log(findSpace(['hello world', 'Ji Ailing', 'cathy', 'yibo', 'Wang Yibo'])) // [ 'hello world', 'Ji Ailing', 'Wang Yibo' ]

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      function getSum (a, b, c) { return a + b + c } const myCurried = curry(getSum) console.log(myCurried(1, 2, 3)) // 6 console.log(myCurried(1)(2, 3))// 6 console.log(myCurried(1, 2)(3))// 6 function curry(fn) { return function curriedFn (...args) { if(args.length < fn.length) { return function () { return curriedFn(...args.concat(Array.from(arguments))) // return fn(...args, ...arguments) // 這樣寫也是一樣的 } }else { return fn(...args) } } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      柯里化可以讓我們給一個(gè)函數(shù)傳遞較少的參數(shù)得到一個(gè)已經(jīng)記住了某些固定參數(shù)的新函數(shù)

      這是一種對(duì)函數(shù)參數(shù)的‘緩存’

      讓函數(shù)變得更靈活,讓函數(shù)的粒度更細(xì)

      可以把多元函數(shù)轉(zhuǎn)換成一元函數(shù),可以組合使用函數(shù)產(chǎn)生強(qiáng)大的功能

      五、函數(shù)組合

      如果一個(gè)函數(shù)要經(jīng)過多個(gè)函數(shù)處理才能得到最終值,這個(gè)時(shí)候可以把中間過程的函數(shù)合并成一個(gè)函數(shù)。

      函數(shù)就像是數(shù)據(jù)的管道,函數(shù)組合就是把這些管道連接起來,讓數(shù)據(jù)穿過多個(gè)管道形成最終結(jié)果

      函數(shù)組合默認(rèn)是從右到左執(zhí)行

      function compose(f, g) { return function (value) { return f(g(value)) } } function reverse (arr) { return arr.reverse() } function first (arr) { return arr[0] } const last = compose(first, reverse) console.log(last([1, 2, 3, 4])) // 4

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      lodash中組合函數(shù)flow()或者flowRight(),他們都可以組合多個(gè)函數(shù)

      flow()是從左到右運(yùn)行

      flowRight()是從右到左運(yùn)行,使用的更多一些

      const _ = require('lodash') const reverse = arr => arr.reverse() const first = arr => arr[0] const toUpper = str => str.toUpperCase() const f = _.flowRight(toUpper, first, reverse) console.log(f(['one', 'two', 'three'])) // THREE

      1

      2

      3

      4

      5

      6

      7

      8

      9

      模擬實(shí)現(xiàn)flowRight:

      // function compose (...args) { // return function (value) { // return args.reverse().reduce(function (acc, fn) { // return fn(acc) // }, value) // } // } // 將上面的寫法修改成箭頭函數(shù) const compose = (...args) => value => args.reverse().reduce((acc, fn) => fn(acc), value) const reverse = arr => arr.reverse() const first = arr => arr[0] const toUpper = str => str.toUpperCase() const f = compose(toUpper, first, reverse) console.log(f(['one', 'two', 'three'])) // THREE

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      我們既可以把g和h組合,還可以把f和g組合,結(jié)果都是一樣的

      const _ = require('lodash') const f = _.flowRight(_.toUpper, _.first, _.reverse) console.log(f(['one', 'two', 'three'])) // THREE const f2 = _.flowRight(_.flowRight(_.toUpper, _.first), _.reverse) console.log(f2(['one', 'two', 'three'])) // THREE const f3 = _.flowRight(_.toUpper, _.flowRight(_.first, _.reverse)) console.log(f3(['one', 'two', 'three'])) // THREE

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      // NEVER SAY DIE --> never-say-die const _ = require('lodash') const split = _.curry((sep, str)=>_.split(str, sep)) // 為什么要調(diào)換兩個(gè)參數(shù)的位置?因?yàn)橐WC函數(shù)只有一個(gè)參數(shù)的函數(shù),那就要通過柯里化實(shí)現(xiàn)。 // 而柯里化想要保留一個(gè)參數(shù),那就只能保留最后一個(gè)參數(shù),所以要把str放到最后 const join = _.curry((sep, arr) => _.join(arr, sep)) const map = _.curry((fn, arr) => _.map(arr, fn)) const log = v => { console.log(v) return v } const trace = _.curry((tag, v) => { console.log(tag, v) return v }) // const f = _.flowRight(join('-'), log, _.toLower, split(' ')) // n-e-v-e-r-,-s-a-y-,-d-i-e // const f = _.flowRight(join('-'), log, split(' '), _.toLower) // never-say-die const f = _.flowRight(join('-'), trace('map之后'), map(_.toLower), trace('split之后'), split(' ')) // never-say-die console.log(f('NEVER SAY DIE'))

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      lodash的fp模塊提供了實(shí)用的對(duì)函數(shù)式編程友好的方法, 函數(shù)優(yōu)先,數(shù)據(jù)在后

      提供了不可變auto-curried iteratee-first data-last的方法

      // lodash的fp模塊 // NEVER SAY DIE --> never-say-die const fp = require('lodash/fp') const f = fp.flowRight(fp.join('-'), fp.map(fp.toLower), fp.split(' ')) console.log(f('NEVER SAY DIE')) // never-say-die

      1

      2

      3

      4

      5

      6

      7

      lodash與lodash-fp中map的區(qū)別

      const _ = require('lodash') const fp = require('lodash/fp') // lodash中的map中的函數(shù)的參數(shù)有三個(gè):(item, index, array) console.log(_.map(['23', '8', '10'], parseInt)) // [ 23, NaN, 2 ] // parseInt('23', 0, array) 第二個(gè)參數(shù)是0,則是10進(jìn)制 // parseInt('8', 1, array) 第二個(gè)參數(shù)是1,不合法,輸出NaN // parseInt('10', 2, array) 第二個(gè)參數(shù)是2,表示2進(jìn)制,輸出2 // lodashFp中的map中的函數(shù)的參數(shù)有1個(gè):(item) console.log(fp.map(parseInt, ['23', '8', '10'])) // [ 23, 8, 10 ]

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      我們可以在數(shù)據(jù)處理的過程中定義成與數(shù)據(jù)無關(guān)的合成運(yùn)算,不需要用到代表數(shù)據(jù)的那個(gè)參數(shù),只要把簡(jiǎn)單的運(yùn)算步驟合成到一起,在使用這種模式之前我們需要定義一些輔助的基本運(yùn)算函數(shù)。

      不需要指明處理的函數(shù)

      只需要合成運(yùn)算過程

      需要定義一些輔助的基本運(yùn)算函數(shù)

      // point free 函數(shù)的組合 // Hello World => hello_world const fp = require('lodash/fp') const f = fp.flowRight(fp.replace(/\s+/g, '_'), fp.toLower) console.log(f('Hello World')) // hello_world

      1

      2

      3

      4

      5

      6

      7

      案例:把一個(gè)字符串中的首字母提取并轉(zhuǎn)換成大寫,使用. 作為分隔符

      // world wild web ==> W. W. W const fp = require('lodash/fp') // const firstLetterToUpper = fp.flowRight(fp.join('. '), fp.map(fp.first), fp.map(fp.toUpper), fp.split(' ')) const firstLetterToUpper = fp.flowRight(fp.join('. '), fp.map(fp.flowRight(fp.first, fp.toUpper)), fp.split(' ')) console.log(firstLetterToUpper('world wild web')) // W. W. W

      1

      2

      3

      4

      5

      六、Functor(函子)

      容器:包含值和值的變形關(guān)系(這個(gè)變性關(guān)系就是函數(shù))

      函子:是一個(gè)特殊的容器,通過一個(gè)普通的對(duì)象來實(shí)現(xiàn),該對(duì)象具有map方法,map方法可以運(yùn)行一個(gè)函數(shù)對(duì)值進(jìn)行處理(變性關(guān)系)

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

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      使用靜態(tài)方法創(chuàng)建對(duì)象:

      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+2) .map(x => x*x) console.log(r) // Container { _value: 49 }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      函數(shù)式編程的運(yùn)算不直接操作值,而是由函子完成

      函子就是一個(gè)實(shí)現(xiàn)了map契約的對(duì)象

      我們可以把函子想象成一個(gè)盒子,這個(gè)盒子封裝了一個(gè)值

      想要處理盒子中的值,我們需要給盒子的map方法傳遞一個(gè)處理值的函數(shù)(純函數(shù)),由這個(gè)番薯來對(duì)值進(jìn)行處理

      最終map方法返回一個(gè)包含新值的盒子(函子)

      普通函子出現(xiàn)異常會(huì)變得不純,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 === undefined } } let r = MayBe.of('hello') .map(x => x.toUpperCase()) console.log(r) // MayBe { _value: 'HELLO' } let r2 = MayBe.of(null) .map(x => x.toUpperCase()) console.log(r2) // MayBe { _value: null } let r3 = MayBe.of('hello world') .map(x => x.toUpperCase()) .map(x => null) .map(x => x.split(' ')) console.log(r3) // MayBe { _value: null } 無法知道null是哪里發(fā)生的

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      Either 兩者中的任何一個(gè),類似于if…else…的處理

      異常會(huì)讓函數(shù)變得不純,Either函子可以用來做異常處理

      Left存異常信息,Right存正常信息

      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)) } } let r1 = Right.of(12) .map(x => x + 2) console.log(r1) let r2 = Left.of(12) // Right { _value: 14 } .map(x => x + 2) console.log(r2) // Left { _value: 12 } function parseJSON (str) { try { return Right.of(JSON.parse(str)) } catch (e) { return Left.of({error: e.message}) } } let r3 = parseJSON('{name: jal}') console.log(r3) // Left { _value: { error: 'Unexpected token n in JSON at position 1' } } let r4 = parseJSON('{"name": "jal"}') console.log(r4) // Right { _value: { name: 'jal' } } let r5 = r4.map(x => x.name.toUpperCase()) console.log(r5) // Right { _value: 'JAL' }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      45

      46

      47

      48

      49

      IO函子中的_value是一個(gè)函數(shù),這里是把函數(shù)作為值來處理

      IO函子可以把不純的動(dòng)作存儲(chǔ)到_value中,延遲執(zhí)行這個(gè)不純的操作(惰性執(zhí)行),包裝當(dāng)前的操作純

      把不純的操作交給調(diào)用者處理

      const fp = require('lodash') 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)) } } const r = IO.of(process).map(p => p.execPath) console.log(r) // IO { _value: [Function (anonymous)] } console.log(r._value()) // /usr/local/Cellar/node/13.6.0/bin/node

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      Task函子處理異步執(zhí)行

      異步任務(wù)的實(shí)現(xiàn)過于復(fù)雜,我們使用folktale中的Task來演示

      folktale:一個(gè)標(biāo)準(zhǔn)的函數(shù)式編程庫(kù)

      和lodash、ramda不同的是,他沒有提供很多功能函數(shù)

      只提供了一些函數(shù)式處理的操作,例如:compose、curry等,一些函子Task、Either、MayBe等

      // folktale的使用 const { compose, curry } = require('folktale/core/lambda') const { toUpper, first } = require('lodash/fp') // curry 第一個(gè)參數(shù)寫上參數(shù)的個(gè)數(shù) const f = curry(2, (x, y) => x + y) console.log(f(1, 2)) // 3 console.log(f(1)(2)) // 3 // folktale中的compose相當(dāng)于lodash中的flowRight const f2 = compose(toUpper, first) console.log(f2(['one', 'two'])) // ONE

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      folktale(2.3.2)2.x中的Task和1.0中的Task區(qū)別很大,1.0這種的用法更接近我們現(xiàn)在演示的函子

      這里以2.3.2來演示

      // Task 處理異步任務(wù) const fs = require('fs') const { task } = require('folktale/concurrency/task') // 柯里化的方法:split、find const { split, find } = require('lodash/fp') function readFile (filename) { // 返回一個(gè)函子 return task(resolver => { fs.readFile(filename, 'utf-8', (err, data) => { if (err) { resolver.reject(err) } resolver.resolve(data) }) }) } readFile('package.json') .map(split('\n')) .map(find(x => x.includes('version'))) .run() .listen({ onRejected: err => { console.log(err) }, onResolved: value => { console.log(value) // "version": "1.0.0", } })

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      Pointed函子是實(shí)現(xiàn)了of靜態(tài)方法的函子

      of方法是為了避免使用new來創(chuàng)建對(duì)象,更深層的含義是of方法用來把值放到上下文Context(把值放到容器中,使用map來處理值)

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

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      Monad函子是可以變扁的Pointed函子,IO(IO(x))

      一個(gè)函子如果具有join和of兩個(gè)方法并遵守一些定律就是一個(gè)Monad

      const fp = require('lodash') const fs = require('fs') 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)) } join () { return this._value() } // 當(dāng)fn返回一個(gè)函子的時(shí)候,用flatMap拍平 flatMap (fn) { return this.map(fn).join() } } const readFile = function (filename) { return new IO(function () { return fs.readFileSync(filename, 'utf-8') }) } const print = function (x) { return new IO(function () { console.log(x) return x }) } // const cat = fp.flowRight(print, readFile) // // IO(IO(x)) // // const r = cat('package.json')._value() // IO { _value: [Function (anonymous)] } // const r = cat('package.json')._value()._value() const r = readFile('package.json') .map(fp.toUpper) .flatMap(print) .join() console.log(r)

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      45

      46

      47

      48

      49

      50

      51

      web前端 數(shù)據(jù)結(jié)構(gòu)

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:InnoDB解決幻讀的方案--LBCC&MVCC
      下一篇:深度解析volatile—底層實(shí)現(xiàn)
      相關(guān)文章
      亚洲噜噜噜噜噜影院在线播放| 亚洲精品国产V片在线观看| 亚洲国产成人久久精品99 | 无码欧精品亚洲日韩一区| 亚洲狠狠爱综合影院婷婷| 亚洲成AⅤ人影院在线观看| 久久久亚洲精华液精华液精华液| 中文字幕无码亚洲欧洲日韩| 亚洲人成777在线播放| 亚洲一区二区影视| 亚洲国产精品线观看不卡| 亚洲欧洲国产经精品香蕉网| 亚洲精品无码久久毛片波多野吉衣| 亚洲人成电影福利在线播放| 亚洲人成电影福利在线播放| 亚洲视频日韩视频| 亚洲另类自拍丝袜第1页| 亚洲午夜久久久久久尤物| 亚洲一区二区免费视频| 在线综合亚洲欧洲综合网站| 亚洲人成网站免费播放| 亚洲av无码一区二区三区天堂| 亚洲aⅴ天堂av天堂无码麻豆| 久久精品国产亚洲av品善| www亚洲精品少妇裸乳一区二区| 日产国产精品亚洲系列| 亚洲日韩VA无码中文字幕 | 美腿丝袜亚洲综合| 亚洲精品乱码久久久久久| 亚洲成AV人片在线观看WWW| 亚洲国产高清人在线| 91亚洲精品第一综合不卡播放| 亚洲熟妇色自偷自拍另类| 亚洲伊人久久大香线焦| 亚洲av午夜精品无码专区| 亚洲经典千人经典日产| 国产成人高清亚洲一区91| 亚洲一区二区三区无码影院| 亚洲欧洲∨国产一区二区三区| 亚洲2022国产成人精品无码区 | 另类专区另类专区亚洲|