React前奏,函數式編程基本概念

函數式編程基本概念

  • 寫在以前,這些內容參考自O`REILLY系列圖書《React學習手冊》git

  • 在React中,UI是用純函數表示的,而且在構造DOM時,是以聲明式(與此相對的,是命令式)的方式。而聲明式的編程是函數式編程更廣義的一部分。因此,先熟悉函數式的編程,頗有必要。es6

結合了以下所述,我發現了es6中數組的新語法, 像mapfilterreduce這些,都知足了不可變性,數據轉換,純函數,高階函數等要點,很精髓。編程

1,不可變性

  • 數據在應用程序中,在不改變原生數據結構的前提下,在此基礎上拷貝後進行編輯。
  • 在此基礎上,就會有淺拷貝和深拷貝的選擇。

好比,在原有對象上添加屬性,而不影響原對象數組

let person = {
  name: 'Beckham',
  age: 33
}

let player = (person, sex) => ({
  ...person,
  sex
})
console.log(person)
console.log(player(person, 'male'))
複製代碼

2,純函數

結果只依賴輸入參數瀏覽器

  • 函數至少接收一個參數
  • 返回一個值或其餘函數
  • 不該該修改或影響傳遞給它的參數
  • 不會產生反作用,好比修改了全局變量,影響了程序的狀態

3,數據轉換

3.1,定義

  • 從其餘數據源建立一個新的數據集。

3.2,做用

  • 爲了使用轉換後的副本

因此,mapfilterreduce這些函數都是必不可少的。bash

  • 基本用法

對象轉爲數組數據結構

let country = {
  "beijing": 10,
  "shanghai": 6,
  "shenzhen": 9
}
let obj = Object.keys(country).map(key => 
({
  name: key,
  value: country[key]
})
)
console.log(obj)
複製代碼

數組中,尋找最大值app

let ages = [11,66,33,22,11,55]
let maxAge = ages.reduce((max, age) => {
if (max > age) {
  return max
} else {
  return age
}
}, 0)
console.log(maxAge)
複製代碼

數組去重,思路:有的話不變,沒有的話添加。函數式編程

let colors = ['red','green','blue','red','green','blue']
const distinctColor = colors.reduce((distinct, color) => (
  (distinct.indexOf(color) !== -1) ? distinct : [...distinct, color]
),
[]
)
console.log(distinctColor)
複製代碼

4,高階函數

  • 定義:將函數做爲參數傳遞,或返回一個函數

因此,數組的mapfilterreduce都是高階函數函數

5,遞歸

  • 目的:在涉及到循環時,遞歸可提供一種替代性的方案
  • 在瀏覽器的調用堆棧中,會依次放入當前執行的函數,在出棧時,後進先出。

打印輸出10~0

let countDown = (count, fn) => {
  fn(count)
  return (count > 0) ? countDown(count-1, fn) : count
}
countDown(10, count => console.log(count))
複製代碼

倒計時輸出10~0

let countDown = (count, fn, delay = 1000) => {
  fn(count)
  return (count > 0) ? setTimeout(() => countDown(count-1, fn), delay) : count
}
countDown(10, count => console.log(count))
複製代碼

6,合成

6.1 定義

  • 將具體的業務邏輯拆解爲小型純函數,用戶會在特定條件下合成它們,以串聯或並聯的方式進行調用。

6.2 目標

  • 經過整合若干簡單函數,構造一個更高階的函數

鏈式調用,就是合成技術之一

let template = 'hh:mm:ss tt'
let clockTime = template.replace('hh','09')
  .replace('mm','06')
  .replace('ss', '52')
  .replace('tt', 'PM')
console.log(clockTime)  // 09:06:52 PM
複製代碼

7,綜上應用

  • 獲取當前時間,實現實時時鐘。

git圖的緣由,時間看起來加速了。實際運行代碼顯示是正確的。

// 計時器時間
const oneSecond = () => 1000

// 獲取當前時間
const getCurrentTime = () => new Date()

// 清除控制檯輸出
const clear = () => console.clear()

// 控制檯打印內容
const log = message => console.log(message)

// 構造一個時鐘對象,包含時分秒
const abstractClockTime = date =>
    ({
        hours: date.getHours(),
        minutes: date.getMinutes(),
        seconds: date.getSeconds()
    })

// 接收一個時鐘對象,
// 該方法用於指定12小時制
const civilianHours = clockTime =>
    ({
        ...clockTime,
        hours: (clockTime.hours > 12) ? clockTime.hours - 12 : clockTime.hours
    })

// 接收一個時鐘對象
// 該方法用於,爲時鐘對象添加一個屬性,用於表示am 或 pm
const appendAMPM = clockTime =>
    ({
        ...clockTime,
        ampm: (clockTime.hours >= 12) ? "PM" : "AM"
    })

/*
* @target 目標函數(本例中就是log函數)
* @time 時鐘對象
* 返回的函數,會將時間發送給目標函數
* */
const display = target => time => target(time)

/*
* @format 模板字符串 "hh:mm:ss:tt"
* @time 時鐘對象
* 返回的函數,將時間進行格式化
* */
const formatClock = format =>
    time =>
        format.replace("hh", time.hours)
            .replace("mm", time.minutes)
            .replace("ss", time.seconds)
            .replace("tt", time.ampm)
/*
* @key 時鐘對象的屬性
* @clockTime 時鐘對象
* 返回的函數,將時鐘對象的屬性,包括時分秒,當<10時,加0
* */
const prependZero = key => clockTime =>
    ({
        ...clockTime,
        [key]: (clockTime[key] < 10) ? "0" + clockTime[key] : clockTime[key]
    })

/*
* 接收參數爲多個函數,返回一個獨立的函數
*   compose函數被調用,返回的獨立函數進行調用時,若是不傳參,
*   就以arg做爲reduce的起始值,arg在使用時是undefined,
*   而一個函數在定義時,若是沒有設置形參,該函數在調用時,傳遞的參數無效。
*   在這個栗子中,...fns爲多個函數組成的數組。
*
* 也就是說,arg做爲第一個函數的參數,若是該函數定義時沒有指定形參,arg將被忽略,
* 第一個函數執行的結果,做爲第二個函數執行的參數,依次類推。
* */
const compose = (...fns) =>
    (arg) =>
        fns.reduce(
            (composed, f) => f(composed),
            arg
        )

const convertToCivilianTime = clockTime =>
    compose(
        appendAMPM,
        civilianHours
    )(clockTime)

const doubleDigits = civilianTime =>
    compose(
        prependZero("hours"),
        prependZero("minutes"),
        prependZero("seconds")
    )(civilianTime)

/*
* compose已經被調用了,以後每隔1s,調用一次compose的執行結果
* 注意參與合成的函數的順序。
*
* 清除打印臺,獲取時間,構造時鐘對象,添加am或pm,12小時制,加0,格式化時分秒,發送給打印函數
* */
const startTicking = () =>
    setInterval(
        compose(
            clear,
            getCurrentTime,
            abstractClockTime,
            convertToCivilianTime,
            doubleDigits,
            formatClock("hh:mm:ss tt"),
            display(log)
        ),
        oneSecond()
    )

startTicking()
複製代碼
相關文章
相關標籤/搜索