函數式編程5-對象校驗器

對象校驗器

本章全部代碼,均在github.com/antgod/func…前端

咱們來解決一個js的廣泛需求。js誕生時,js的做用僅僅一個前端作表單檢查的校驗器。現在,各類校驗框架早已成熟,可是咱們仍然有大量的需求須要校驗對象。好比後端返回的json咱們須要校驗,前端提交的json咱們須要校驗。數據管理中的store咱們須要校驗,或者說,任意一個函數,咱們都須要對入參進行校驗。git

不少框架或者函數,直接把校驗寫在寫在函數的最頂端。或許有的時候,咱們會抽出一個公共函數來校驗,好比這樣:github

const grund = (checker, handle, errorCallback = args => args) => (...args) => {
  const result = checker(...args)
  if (result.length) {
    errorCallback(result)
  } else {
    return handle(...args)
  }
}

const handle = (...args) => {
  console.log('正確處理', args)
  return '處理完畢'
}

const checker = (...args) => !args.length ? ['該函數的參數不能爲空'] : []

grund(checker, handle, errors => console.log(errors))()複製代碼

grund函數用來檢驗傳入參數是否正確,若是正確,執行正確的處理邏輯,若是錯誤,執行異常處理邏輯。json

因爲checker過於簡單,這並不能知足咱們的需求。有時候,咱們常常要根據多組條件校驗,這就須要多個校驗器。而把checker當作數組傳入grund顯然不是一個好方法。咱們須要在checker函數中就傳入多組校驗器,而checker的工做就是負責把全部校驗器合成運行,返回運行結果便可。redux

const checker = (...validators) => (...args) => {
  return validators.reduce((errors, validator) => {
    return validator(...args) ? errors : [...errors, validator.message]
  }, [])
}複製代碼

注意這裏咱們使用了每一個校驗器的message屬性,這彷佛是潛規則,沒錯,這相似於先後端接口的約定字段success,message。幾乎全部接口都叫這兩個字段(即便不一樣,也大同小異)。咱們調用時候,須要給每一個校驗器加上message字段。後端

const validator1 = () => false
validator1.message = '校驗1不經過'

const validator2 = () => false
validator2.message = '校驗2不經過'
checker(validator1, validator2)({})複製代碼

好了,效果達成了。但是每一個vdalidator都要設置message讓人感到痛苦。若是可以自動添加message豈不是更好?咱們能夠用一個API建立校驗器,讓人一目瞭然。數組

const validator = (handle, message) => {
  const fun = (...args) => handle(...args)
  fun.message = message
  return fun
}複製代碼

API相似於redux的actionCreator。能讓咱們輕鬆建立vilidator。使用時也很是簡單。閉包

const v1 = (object) => {
  return object.a === 1
}

const v2 = (object) => {
  return object.b === 2
}

const v3 = (object) => {
  return object.c === true
}

// test
const c1 = checker(validator(v1, 'error1'), validator(v2, 'error2'), validator(v3, 'error3'))複製代碼

咱們有三個校驗規則,v1,v2,v3,經過validator建立了三個校驗器傳入checker,返回了檢查器c1。代碼很是清晰明瞭。框架

這時候咱們開始驗證。函數

const object = {
  a: 1,
  b: 2,
  c: false,
}

const handle = (...args) => {
  console.log('繼續處理', args)
  return '返回正確'
}

grund(c1, handle, errors => console.log(errors))(object)
// => [ 'error3' ]複製代碼

v3要求c屬性必須是true,可是測試數據的c屬性是false。好,檢查器與校驗器都能正常工做。可咱們若是有這樣的需求,須要object裏面必須有a,b,c,d四個key,怎麼辦呢?

添加簡單的validator固然沒有問題。然而,保持高水平的編碼規則須要一些有趣的技巧。高階函數的本質就是參數能夠做爲返回函數閉包的行爲配置,牢記這一點,可讓你隨時隨地返回須要函數的地方返回配置過的閉包。

以上面的需求爲例。咱們再建立一個所需鍵的簡單列表會更加流暢。爲了讓這個建立列表的函數符合約定,咱們讓他返回一個閉包和一個錯誤。

const hasKeys = (...keys) => {
  const fun = obj => keys.every(key => obj[key] !== undefined)
  fun.message = `object must have value of keys: ${keys}`
  return fun
}複製代碼

你會發現閉包用來檢查給的對象是否有效。hasKeys的目的是爲了向fun函數提供執行配置。此外,經過直接返回一個函數名,咱們很好的描述了需求。從一個函數返回另外一個函數的技術,這個過程當中捕獲參數-被稱爲柯里化。

使用示例以下:

const c1 = checker(validator(v1, 'error1'), validator(v2, 'error2'), validator(v3, 'error3'), hasKeys('a', 'b', 'c', 'd'))

const object = {
  a: 1,
  b: 2,
  c: true,
}

const handle = (...args) => {
  console.log('繼續處理', args)
  return '返回正確'
}

grund(c1, handle, errors => console.log(errors))(object)
// => [ 'object must have value of keys: a,b,c,d' ]複製代碼

固然,咱們也能夠直接運行c1函數。

const c1 = checker(validator(v1, 'error1'), validator(v2, 'error2'), validator(v3, 'error3'), hasKeys('a', 'b', 'c', 'd'))


const object1 = {
  a: 1,
  b: 2,
}

const object2 = {
  a: 1,
  b: 2,
  c: true,
  d: 1,
}

console.log(c1(object1))
// => [ 'error3', 'object must have value of keys: a,b,c,d' ]
console.log(c1(object2))
// => []複製代碼

在任何狀況下,使用c1檢查器構建語法一致性,也就是說,在校驗器固定的狀況下,檢查器會按照校驗器所定義的方式校驗,咱們只須要傳入須要校驗的參數便可。

相關文章
相關標籤/搜索