函数式编程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检查器构建语法一致性,也就是说,在校验器固定的状况下,检查器会按照校验器所定义的方式校验,咱们只须要传入须要校验的参数便可。

相关文章
相关标签/搜索