javascript 資料型態/結構驗證庫 : Skeletons

Skeletons 是一個簡單直覺的純JS資料驗證庫javascript

前言

當初專案須要將資料以JSON格式儲存在本地端,萬一資料結構出了問題或是不符合預其,後面程式都會出問題,所以想寫一個簡單直覺的純JS資料驗證方法,並開源到npm上。java

但願對你們有幫助。喜歡能夠給個星:) 有任何討論都歡迎。git

源碼

Javascript 型態

先來介紹一下Javascript有趣的資料型態,若有錯誤請幫忙提出修正~github

JS 共有七種資料型態npm

其中包含六種 Primitive types :app

  • Boolean
  • Null
  • Undefined
  • Number
  • String
  • Symbol

和 Object函數

特別的是 Function 廣義來講也是屬於物件。測試

咱們能夠用 typeof 來檢查型態 (回傳一個字串)ui

typeof 1 // 'number'
typeof "" // 'string'
typeof true // 'boolean'
typeof undefined // 'undefined'
typeof null // 'object'
typeof Symbol // 'symbol'
typeof function(){ } // 'function'
typeof {} // 'object'
複製代碼

等等,你有沒有發現什麼端倪?咱們一個一個看。this

Number

通常咱們會直接定義變數:

let a = 1
複製代碼

也能夠用 function 定義

let a = Number(1)
typeof a // 'number'
複製代碼

但若是加上 new ,則會建立一個Number物件

let a = new Number(1)
typeof a // 'object'
複製代碼

NaN 也是屬於Number

typeof NaN // 'number'
複製代碼

若是要判斷變數a是可計算的數字且不是NaN,如下是行不通的

if(typeof a==='number' && a!==NaN) //... a !== NaN 永遠是 true
複製代碼

由於 NaN 很特別,它不會等於任何值

// 如下統統都是 false !
NaN == 0 
NaN == false
NaN == NaN
NaN === NaN
複製代碼

你可能會想到用 boolean 來判斷 true/false

if(typeof a==='number' && !a) // ...
複製代碼

可是別忘了還有個 0 :

if(typeof a==='number' && !a && a !== 0) // ...
複製代碼

固然最簡單的是用 isNaN 這個方法區分

if(typeof a==='number' && !isNaN(a)) // ...
複製代碼

Boolean, String

和 Number 很像,注意用new的話會同樣是建立object。

let a = true  // boolean
a = Boolean(true) //boolean
a = new Boolean(true) //object
複製代碼

Undefined

也是一種premitive type

undefinednull 是沒有 funtion的 ,直接指定值就好

typeof undefined // 'undefined'
複製代碼

值得一提的是,雖然如下都是否認值 (false)

Boolean(0)
Boolean(false)
Boolean('')
Boolean(undefined)
Boolean(null)
複製代碼

但動態型別方面0, false, '' 是一夥的, undefined, null則是另一個團體

0 == false  // true
0 == ''     // true
false == '' // true

undefined == null // true

undefined == false // false
undefined == 0 //false
null == '' //false
複製代碼

Null

null也是一種type,可是。。。

typeof null // 'object'
複製代碼

沒錯,typeof 打印出來的是 'object'

上網查了一下,有些人說是JS當初設計的錯誤。

咱們要判斷一個變數是物件的話能夠這樣:

if(typeof a === 'object' && a!==null) // ...
複製代碼

Symbol

最後一個 premitive type symbol

建立一個 symbol:

let a = Symbol()
typeof a // 'symbol'
複製代碼

注意不能用 new,會丟出錯誤

let a = new Symbol()
> Uncaught TypeError: Symbol is not a constructor
複製代碼

Object

除了上面六種 primitive type,其餘都歸類爲物件

但特別的是 function,使用typeof檢查會回傳function字串:

typeof function(){} // 'function'
複製代碼

讓咱們能很好的區別 function 和其餘通常的物件

Skeletons

接下來要介紹這個庫了,有興趣的話能夠先看看介紹

請先記好上面 Javascript 原生定義的資料型別,由於這個庫的分類有些不同

Skeletons 中可定義的類型除了本來的七種JS類型,額外分出 array 和 function

緣由是這兩個都是很經常使用的,將他們從物件特別區分出來。

使用方法

定義一個規則,使用 validate 來驗證資料

const rule = new Skeletons(schema)
rule.validate(data)
複製代碼

Schema

定義規則須要傳入一個schema,也就是你設想的資料結構以及形態

schema 能夠有四種

1. premitive type function

共有四種能夠用 (undefined和null是沒有function的,咱們後面談如何定義)

  • Number
  • Boolean
  • String
  • Symbol

分別定義四種形態,使用上不用呼叫,直接傳入function

以下,定義一個型態爲數字的schema

const schema = Number
複製代碼

2. 使用 object literal

使用最值覺的 object literal 來定義一個物件 (注意,在Skeletons會排除array和function)的key

每一個key均可指派另外一個schema

以下定義了一個有 x, y 兩個鍵的物件,且兩個鍵的值都是數字型態

const schema = {
  x: Number,
  y: Number
}
複製代碼

使用這種方式,讓你可以輕易地定義結構較深的物件

const userSchema = {
  name: String,
  id: String,
  VIP: {
    code: Number,
    details: {
      type: String,
      level: Number,
      expired: Boolean
    }
  },
}
複製代碼

3. array literal

使用array literal來定義有固定元素數量的array

const schema = [String, Number, Skeletons.Function()]
複製代碼

4.呼叫Skeletons的靜態方法

  • Skeletons.Number()
  • Skeletons.String()
  • Skeletons.Boolean()
  • Skeletons.Null()
  • Skeletons.Symbol()
  • Skeletons.Any()
  • Skeletons.Array()
  • Skeletons.Object()
  • Skeletons.Function()
  • Skeletons.MapObject()

共有十種方法,分別表明是五種premitive type (不含undefined)、Object, 從物件中分出來的 Array, Function,以及特殊的Any(任何非undefined的型態) 和 MapObject

每種方法都接受一個options物件當作參數,

且均可定義三個基本的property

  • options.required

    type: Boolean

    default: true

    Skeletons對於任何 undefined 值都會認定爲驗證失敗:

    new Skeletons({
      a: Number
    }).validate({})
    // data.a got undefined, validation filed
    複製代碼

    若是要容許該層資料能夠爲 undefined,設 options.required 爲 false

    new Skeletons({
      a: Skeletons.Number({
        required: false
      })
    })
    複製代碼
  • options.default

    type: 任何

    default: undefined

    有時後資料的預設值(或者空值)的型態可能會和資料有值的時後不同,比方說有人可能會用null來替代空的物件。

    new Skeletons(Skeletons.Object({
      default: null
    }))
    複製代碼
  • options.validator

    type: Function

    傳入一個function,回傳true/false來驗證資料

    validator(value, data)
    複製代碼

    該函數可接收兩個參數:value表明該層資料的值,data表明整個資料

    如下這個例子,value等於120, data等於整個datasource

    const datasource = {
      a: 120,
      b: 240
    }
    
    new Skeletons({
      a: Skeletons.Number({
        validator: (val, data) => {
         // in this case, val = 120, data = datasource
         return val === data.b*2
        }
      }),
      b: Number
    })
    複製代碼

更多詳細的介紹能夠參考文件

驗證

驗證可分爲

  • 使用 console 打印出錯誤資訊
  • 直接拋出錯誤

如何設定可參考文件

每次驗證後,可由warnings屬性得到錯誤資訊

const rule = new Skeletons(Number)
rule.validate('1')
rule.warnings // 一串array 包含全部錯誤資訊
複製代碼

關於錯誤資訊可參考warnings

示例

接下來演示一些資料定義的範例

範例一 : 陣列

定義一個schema表明不是NaNnumber

// ex: 1
const calcNum = Skeletons.Number({
  allowNaN: false
})
複製代碼

定義一個array,每一個元素是含有x,y屬性,值爲非NaN數字的物件

// ex: [{x: 1, y: 2}, {x: 3, y: 5}]
new Skeletons(
  Skeletons.Array({
    item: {
      x: calcNum,
      y: calcNum
    }
  })
)
複製代碼

規定array必定要有元素

// ex: [{x: 1, y: 2}, {x: 3, y: 5}]
new Skeletons(
  Skeletons.Array({
    validator: ary=>ary.length>0,
    item: {
      x: calcNum,
      y: calcNum
    }
  })
)
複製代碼

範例二 : 和其餘資料比對

假設有一筆資料,當age大於 18,grownup等於true表明已成年,反之則爲false

const ex = {
  age: 19,
  grownup: true
}
複製代碼

咱們能夠用 validator來進行檢查

new Skeletons(
  {
    age: Number,
    grownup: Skeletons.Boolean({
      validator: (val, data) => val === data.age>=18
    })
  }
).validate(ex)
複製代碼

範例三: 不限制物件的key

有時後物件做爲一個相似map來除存對應的key,表明並無固定的屬性值和數量。這時可使用 MapObject

例如 room以房間的id當作key來mapping

const room = {
  idkfd: {
    name : 'have fun',
    members: 4,
    id: 'idkfd'
  },
  ckclo: {
    name : 'My room',
    members: 2,
    id: 'ckclo'
  }, 
  ppqkd: {
    name : 'User0001\'s room',
    members: 8,
    id: 'ppqkd'
  } 
}
複製代碼

可這樣定義

new Skeletons(
  Skeletons.MapObject({
    keyValidator: (k, data)=> k === data[k].id,
    item: {
      name: String,
      members: Number,
      id: String
    }
  })
)
複製代碼

結語

就先介紹到這,這個庫比較像是開發階段、測試使用的,可確保資料的結構、型態符合本身的要求,避免後續程序出錯,

並但願用直覺簡單的方式就能定義複雜的結構。

但願對你們能有所幫助,謝謝。

相關文章
相關標籤/搜索