React前奏,函數語言程式設計基本概念

非梧不棲發表於2018-06-25

函數語言程式設計基本概念

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

  • 在React中,UI是用純函式表示的,並且在構造DOM時,是以宣告式(與此相對的,是命令式)的方式。而宣告式的程式設計是函數語言程式設計更廣義的一部分。所以,先熟悉函式式的程式設計,很有必要。

結合瞭如下所述,我發現了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這些函式都是必不可少的。

  • 基本用法

物件轉為陣列

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

陣列中,尋找最大值

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圖的原因,時間看起來加速了。實際執行程式碼顯示是正確的。

React前奏,函數語言程式設計基本概念

// 計時器時間
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()
複製程式碼

相關文章