真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

JavaScript函數(shù)式編程之函子

函子(Functor)

函子是一個(gè)特殊的容器,通過(guò)一個(gè)普通對(duì)象來(lái)實(shí)現(xiàn),該對(duì)象具有map方法,map方法可以運(yùn)行一個(gè)函數(shù)對(duì)值進(jìn)行處理(變形關(guān)系),容器包含值和值變形關(guān)系(這個(gè)變形關(guān)系就是函數(shù))。函數(shù)式編程中解決副作用的存在

網(wǎng)站建設(shè)哪家好,找成都創(chuàng)新互聯(lián)公司!專注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、微信平臺(tái)小程序開(kāi)發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了建鄴免費(fèi)建站歡迎大家使用!

  • 函數(shù)式編程的運(yùn)算不直接操作值,,而是由函子完成
  • 函子就是一個(gè)實(shí)現(xiàn)了map契約的對(duì)象
  • 我們可以把函子想象成一個(gè)盒子,盒子里面封裝了一個(gè)值
  • 想要處理盒子中的值,我們需要給盒子的map方法傳遞一個(gè)處理值的函數(shù)(純函數(shù)),由這個(gè)函數(shù)來(lái)對(duì)值進(jìn)行處理
  • 最終map方法返回一個(gè)包含新值所在的盒子(函子)

根據(jù)函子的定義我們創(chuàng)建一個(gè)函子

// functor 函子
class Container {
  constructor (value) {
    // 函子內(nèi)部保存這個(gè)值。下劃線是不想外部訪問(wèn)
    this._value = value
  }

  // map 方法接收一個(gè)處理值的函數(shù)
  map (fn) {
    return new Container(fn(this._value))
  }
}

此時(shí)就已經(jīng)創(chuàng)建了一個(gè)函子但是這是面向?qū)ο蟮姆绞絹?lái)創(chuàng)建的,換成用函數(shù)式編程來(lái)寫(xiě)一個(gè)函子

class Container {
  constructor (value) {
    this._value = value
  }

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

  static of (value) {
    return new Container(value)
  }
}

let x = Container.of(5).map(x => x + 1).map(x => x - 1)

但是這個(gè)函子還是存在一些問(wèn)題,比如空值的時(shí)候就會(huì)報(bào)錯(cuò), 會(huì)讓我們的函子變的不純,我們需要去攔截空值錯(cuò)誤,我們創(chuàng)建一個(gè)方法去判斷是否為空值,如果是控制我們直接返回一個(gè)空值的函子,如果有值再去處理,這個(gè)時(shí)候就需要使用MayBe函子

let x = Container.of(null).map(x => x + 1).map(x => x - 1)

MayBe 函子

我們?cè)诰幊痰倪^(guò)程中可能會(huì)遇到很多錯(cuò)誤,需要對(duì)這些錯(cuò)誤做相應(yīng)的處理,MayBe函子的作用就是可以對(duì)外部的空值情況做處理(控制副作用在允許的范圍)

// MayBe 函子
class MayBe {
  constructor (value) {
    this._value = value
  }

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

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

  static of (value) {
    return new MayBe(value)
  }
}

let x = MayBe.of(null)
  .map(x => x + 1)
  .map(x => x - 1)
console.log(x)

這個(gè)時(shí)候我們已經(jīng)能正常執(zhí)行了,但是現(xiàn)在出現(xiàn)了空值的函子,但是我們不知道那個(gè)地方出現(xiàn)了空值,所以我們創(chuàng)建兩個(gè)函子一個(gè)是正常的處理一個(gè)是出現(xiàn)錯(cuò)誤情況處理,正常的就按照正常的方式創(chuàng)建,錯(cuò)誤的是是否我們把map方法改造一下讓她不再處理回調(diào)函數(shù),直接返回一個(gè)空值的MayBe函子,這樣就記錄下了錯(cuò)誤信息Eitcher 函子就是來(lái)處理這種情況的

Either函子

Eitcher 類似于 if else 的處理,兩者中的任何一個(gè),異常會(huì)讓函數(shù)變的不純,Eitcher函子可以用來(lái)做異常處理

// 因?yàn)槭嵌x一,所以定義兩個(gè)類 Left 和 Right

// 記錄錯(cuò)誤信息的
class Left {
  constructor (value) {
    this._value = value
  }

  map (fn) {
    return this
  }

  static of (value) {
    return new Left(value)
  }
}

// 正常處理
class Rgiht {
  constructor (value) {
    this._value = value
  }

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

  static of (value) {
    return new Rgiht(value)
  }
}

function parseJson (str) {
  try {
    return Rgiht.of(JSON.parse(str))
  } catch (err) {
    return Left.of({ message: err.message })
  }
}

// 故意傳入錯(cuò)誤的數(shù)據(jù)
let r = parseJson('{ name: "2" }')
r.map(x => x.name.toUpperCase())
console.log(r)

IO 函子

IO 函子中的 _value 是一個(gè)函數(shù), 這里把函數(shù)作為值來(lái)處理, IO 函子可以吧不純的動(dòng)作儲(chǔ)存到_value中,延遲這個(gè)不純的操作(惰性執(zhí)行),保證當(dāng)前的操作是純的,延遲把不純的操作到調(diào)用者來(lái)處理

const fp = require('lodash/fp')

// IO 函子
class IO {
  constructor (fn) {
    this._value = fn
  }
  static of (value) {
    return new IO(function () {
      return value
    })
  }
  map (fn) {
    // 把當(dāng)前的value 和傳入的fn 函數(shù)組合成一個(gè)新的函數(shù)
    return new IO(fp.flowRight(fn, this._value))
  }
}

let r = IO.of(process).map(x => x.execPath)

console.log(r)
console.log(r._value())

IO 函子內(nèi)部幫我們包裝了一些函數(shù),當(dāng)我們傳遞函數(shù)的時(shí)候有可能這個(gè)函數(shù)是一個(gè)不純的操作,不管這個(gè)函數(shù)純與不純,IO這個(gè)函子在執(zhí)行的過(guò)程中它返回的這個(gè)結(jié)果始終是一個(gè)純的操作,我們調(diào)用map的時(shí)候始終返回的是一個(gè)函子,但是IO函子這個(gè)_value屬性他里面要去合并很多函數(shù),所以他里面可能是不純的,把這些不純的操作延遲到了調(diào)用的時(shí)候,也就是我們通過(guò)IO函子控制了副作用的在可控的范圍內(nèi)發(fā)生

實(shí)現(xiàn) liunx 下 cat 命令

const fp = require('lodash/fp')

// IO 函子
class IO {
  constructor (fn) {
    this._value = fn
  }
  static of (value) {
    return new IO(function () {
      return value
    })
  }
  map (fn) {
    // 把當(dāng)前的value 和傳入的fn 函數(shù)組合成一個(gè)新的函數(shù)
    return new IO(fp.flowRight(fn, this._value))
  }
}

let r = IO.of(process).map(x => x.execPath)

function readFile (fileName) {
  return new IO(() => fs.readFileSync(fileName, 'utf-8'))
}

function print (x) {
  return new IO(() => {
    console.log(x)
    return x
  })
}

let cat = fp.flowRight(print, readFile)

console.log(cat('package.json')._value()._value())

此時(shí)IO函子出現(xiàn)了嵌套的問(wèn)題,導(dǎo)致調(diào)用嵌套函子中的方法就必須要要._value()._value() 這樣來(lái)執(zhí)了,嵌套了幾層就需要幾層調(diào)用

Folktale

Folktale 是一個(gè)標(biāo)準(zhǔn)的函數(shù)式編程庫(kù),和lodash不同的是,他沒(méi)有提供很多功能函數(shù),只提供了一些函數(shù)式處理的操作,例如:compose、curry等,一些函子 Task、Either、MayBe等,

Folktale 中的currycompose的簡(jiǎn)單使用

const { compose, curry } = require('folktale/core/lambda')
const { toUpper, first } = require('lodash/fp')

// 與lodash區(qū)別,第一個(gè)參數(shù)指明后面參數(shù)的個(gè)數(shù)
let f = curry(2, (n1, n2) => n1 + n2)

console.log(f(1, 2))

// compose 就是函數(shù)組合 lodash 中的函數(shù)組合是 flowRight
let f2 = compose(toUpper, first)

console.log(f2(['one', 'two']))

Folktale 中的 task 函子

函子可以處理異步任務(wù),在異步任務(wù)中會(huì)通往地獄之門(mén)的回調(diào),而使用task 函子可以避免回調(diào)的嵌套,詳細(xì)請(qǐng)看官方文檔

// Task 異步任務(wù)
const { task } = require('folktale/concurrency/task')
const { split, find } = require('lodash/fp')
const fs = require('fs')

function readFile (filename) {
  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')))
  // 執(zhí)行讀取文件
  .run()
  .listen({
    onRejected(err) {
      console.log(err)
    },
    onResolved(value) {
      console.log(value)
    }
  })

Pointed函子

Pointed函子 是實(shí)現(xiàn)了of靜態(tài)方法, of 方法是為了避免使用new 來(lái)創(chuàng)建對(duì)象,更深層次含義是of方法把值放到上下文Context(把值放到容器中,使用map 來(lái)處理值)

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

Monad函子

解決函子嵌套的問(wèn)題,Monad 函子是可以變扁的 Pointed 函子 IO(IO),一個(gè)函子如果具有joinof兩個(gè)方法并遵循一些定律就是一個(gè)Monad

class IO {
  constructor (fn) {
    this._value = fn
  }
  static of (value) {
    return new IO(function () {
      return value
    })
  }
  map (fn) {
    return new IO(fp.flowRight(fn, this._value))
  }

  join () {
    return this._value()
  }

  // 同時(shí)調(diào)用 join 和 map
  flatMap (fn) {
    return this.map(fn).join()
  }
}

function readFile (fileName) {
  return new IO(() => fs.readFileSync(fileName, 'utf-8'))
}

function print (x) {
  return new IO(() => {
    return x
  })
}

let r = readFile('package.json').flatMap(print).join()

console.log(r)

當(dāng)我們想要去調(diào)用一個(gè)方法,這個(gè)方法返回一值的時(shí)候我們?nèi)フ{(diào)用map方法,當(dāng)我們想要去調(diào)用一個(gè)方法,這個(gè)方法返回一個(gè)函子的時(shí)候我們?nèi)フ{(diào)用flatMap方法

原文地址:https://kspf.xyz/archives/17
更多內(nèi)容微信公眾號(hào)搜索充饑的泡飯
小程序搜一搜開(kāi)水泡飯的博客


本文名稱:JavaScript函數(shù)式編程之函子
標(biāo)題來(lái)源:http://weahome.cn/article/dsojiid.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部