実務で使えるJavaScript検証・空値判定・高頻度イベント制御テクニック

1. よく使われる正規表現パターン集

// 画像MIMEタイプ
/^image\/(?:jpe?g|png|gif)$/i.test(file.type)

// 金額(符号付き小数可)
/^[+]?\d+(?:\.\d+)?$/

// 電話番号(中国大陸)
/^(?:(?:\+?86)|(?:17951))?(?:13\d|15[0-35-9]|166|17[0-35-8]|18\d|14[57])\d{8}$/

// 0以上の整数
/^\d+$/

// メールアドレス簡易判定
/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/

2. 中国大陸身分証番号の厳密チェック

// idValidator.js
export function verifyIdCard(id) {
  const str = String(id)

  // ① 書式チェック(15桁または18桁)
  const pattern15 = /^[1-9]\d{7}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])\d{3}$/
  const pattern18 = /^[1-9]\d{5}(?:19|20)\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])\d{3}[\dX]$/
  if (!pattern15.test(str) && !pattern18.test(str)) return false

  // ② 行政区コード確認
  const regionMap = {
    11:'北京',12:'天津',13:'河北',14:'山西',15:'内蒙古',
    21:'辽宁',22:'吉林',23:'黑龙江',31:'上海',32:'江苏',
    33:'浙江',34:'安徽',35:'福建',36:'江西',37:'山东',
    41:'河南',42:'湖北',43:'湖南',44:'广东',45:'广西',
    46:'海南',50:'重庆',51:'四川',52:'贵州',53:'云南',
    54:'西藏',61:'陕西',62:'甘肃',63:'青海',64:'宁夏',
    65:'新疆',71:'台湾',81:'香港',82:'澳门',91:'国外'
  }
  if (!regionMap[str.slice(0,2)]) return false

  // ③ 生年月日妥当性
  let y, m, d
  if (str.length === 15) {
    [, , y, m, d] = str.match(/^(\d{6})(\d{2})(\d{2})(\d{2})\d{3}$/)
    y = (y[0] === '0' ? '20' : '19') + y
  } else {
    [, , y, m, d] = str.match(/^(\d{6})(\d{4})(\d{2})(\d{2})\d{3}[\dX]$/)
  }
  const birth = new Date(y, m - 1, d)
  if (birth > new Date()) return false
  if (new Date().getFullYear() - birth.getFullYear() > 150) return false

  // ④ 18桁の場合、チェックディジット検証
  if (str.length === 18) {
    const weights = [7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2]
    const checkTable = ['1','0','X','9','8','7','6','5','4','3','2']
    const sum = [...str.slice(0,17)].reduce((acc, cur, idx) => acc + cur * weights[idx], 0)
    if (checkTable[sum % 11] !== str[17]) return false
  }
  return true
}

利用例(Vue2)

import { verifyIdCard } from '@/utils/idValidator'

if (this.idCardNo && !verifyIdCard(this.idCardNo)) {
  this.$message.error('身分証番号が正しくありません')
  return
}

3. 空値判定ユーティリティ

// emptyChecker.js
export const isBlank = v => v == null || v === ''

export const isDeepEmpty = val => {
  if (val == null) return true
  if (typeof val === 'function') return true
  if (Array.isArray(val) && val.length === 0) return true
  if (val instanceof Map || val instanceof Set) return val.size === 0
  if (typeof val === 'string' && val.trim() === '') return true
  if (typeof val === 'object' && Object.keys(val).length === 0) return true
  if (Number.isNaN(val)) return true
  return false
}

利用例

import { isBlank } from '@/utils/emptyChecker'

if (isBlank(this.username)) {
  this.$message.error('ユーザー名を入力してください')
  return
}

// リッチテキストの空判定
if (isBlank(this.content) || this.content === '<p><br></p>') {
  this.$message.error('本文を入力してください')
}

4. 高頻度イベントを抑える:Throttle / Debounce

// eventControl.js
// 直近wait ms以内に1回のみ実行
export const throttle = (fn, wait = 300) => {
  let last = 0
  return function (...args) {
    const now = Date.now()
    if (now - last >= wait) {
      last = now
      fn.apply(this, args)
    }
  }
}

// 連続実行を最後の1回に集約
export const debounce = (fn, wait = 300) => {
  let timer
  return function (...args) {
    clearTimeout(timer)
    timer = setTimeout(() => fn.apply(this, args), wait)
  }
}

利用例(Vue3 Composition API)

import { debounce } from '@/utils/eventControl'

const keyword = ref('')
watch(keyword, debounce(() => fetchList(), 500))

タグ: javascript RegExp IdCard debounce throttle

7月2日 00:16 投稿