前端常用工具函数

199 阅读5分钟

前言

由于经常使用一些工具函数,又不想引入太多npm依赖,所以记录常用的工具函数,以便查阅

函数

/**
 * 工具函数库
 */
class Units {
  // 判断是否是移动端访问
  isMobile () {
    var userAgentInfo = navigator.userAgent
    var Agents = ['Android', 'iPhone', 'SymbianOS', 'Windows Phone', 'iPad', 'iPod']
    for (var v = 0; v < Agents.length; v++) {
      if (userAgentInfo.indexOf(Agents[v]) > 0) {
        return true
      }
    }
    return false
  }
  isDate (value) {
    return Object.prototype.toString.call(value) === '[object Date]'
  }
  isArray (value) {
    return Object.prototype.toString.call(value) === '[object Array]'
  }
  isReg (value) {
    return Object.prototype.toString.call(value) === '[object RegExp]'
  }
  isFun (value) {
    return Object.prototype.toString.call(value) === '[object Function]'
  }
  isObeject (value) {
    return Object.prototype.toString.call(value) === '[object Object]'
  }
  // 是否是微信环境
  isWechat () {
    return navigator.userAgent.toLowerCase().indexOf('micromessenger') !== -1
  }
  // 取[begin, end]间的数据整数
  GetRandom (begin, end) {
    var num = Math.random() * 100000000
    return Math.floor(num % (end - begin + 1) + begin)
  }
  // 取len长的随机字符串
  GetRandString (len, range) {
    range = range || '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    var s = range.split('')
    var ret = ''
    for (var i = 0; i < len; i++) {
      ret += s[GetRandom(0, s.length - 1)]
    }
    return ret
  }
  // 取时间戳(秒)
  GetTimestamp (timestr) {
    var t
    if (timestr) {
      t = new Date(timestr)
    } else {
      t = new Date()
    }
    return parseInt(t.getTime() / 1000)
  }
    /* 时间格式转换
    *
    * 对Date的扩展,将 Date 转化为指定格式的String
    * 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符,
    * 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字)
    * 例子:
    * (new Date()).Format('yyyy-MM-dd hh:mm:ss.S') ==> 2006-07-02 08:09:04.423
    * (new Date()).Format('yyyy-M-d h:m:s.S')      ==> 2006-7-2 8:9:4.18
    *
    * 参考: http://www.cnblogs.com/zhangpengshou/archive/2012/07/19/2599053.html
    */
  TimeTo (timestamp, format) {
    var t = timestamp > 0 ? new Date(+timestamp) : new Date()
    var o = {
      'M+': t.getMonth() + 1, //月份
      'd+': t.getDate(), //日
      'h+': t.getHours(), //小时
      'm+': t.getMinutes(), //分
      's+': t.getSeconds(), //秒
      'q+': Math.floor((t.getMonth() + 3) / 3), //季度
      S: t.getMilliseconds() //毫秒
    }
    var fmt = format || 'yyyy.MM.dd hh:mm:ss'
    if (/(y+)/.test(fmt)) {
      fmt = fmt.replace(
        RegExp.$1,
        (t.getFullYear() + '').substr(4 - RegExp.$1.length)
      )
    }
    for (var k in o) {
      if (new RegExp('(' + k + ')').test(fmt)) {
        fmt = fmt.replace(
          RegExp.$1,
          RegExp.$1.length === 1
            ? o[k]
            : ('00' + o[k]).substr(('' + o[k]).length)
        )
      }
    }
    return fmt
  }
  // 调整控件高度,使页面不因控件太高而出现滚动条
  AdjustHeight (elem) {
    elem.style.overflowY = 'auto'
    elem.style.height = document.body.clientHeight + 'px'
    const HasScroll = function() {
      return (
        document.body.scrollHeight >
        (window.innerHeight || document.documentElement.clientHeight)
      )
    }
    if (!HasScroll()) {
      return
    }
    let cur = document.body.clientHeight
    let step = cur / 2
    cur += 10

    for (let i = 0; i < 100; i++) {
      if (step < 1 || cur < 0) {
        break
      }
      // 如果还有滚动条,高度再减小
      let hasScroll = HasScroll()
      if (hasScroll) {
        cur -= step
      } else {
        step = step / 2
        cur += step
      }
      cur = parseInt(cur)
      if (cur > 0) {
        elem.style.height = cur + 'px'
      }
    }
    if (cur <= 20) {
      cur = ''
    } else {
      cur = cur + 'px'
    }
    elem.style.height = cur + 'px'
  }
  // 解析地址栏url参数
  parseURL (url) {
    var a = document.createElement('a')
    a.href = url
    return {
      source: url,
      protocol: a.protocol.replace(':', ''),
      host: a.hostname,
      port: a.port || '80',
      query: a.search,
      params: (function() {
        var ret = {},
          seg = a.search.replace(/^\?/, '').split('&'),
          len = seg.length,
          i = 0,
          s
        for ( ; i < len; i++) {
          if (!seg[i]) {
            continue
          }
          s = seg[i].split('=')
          ret[s[0]] = s[1]
        }
        return ret
      })(),
      file: (a.pathname.match(/\/([^\/?#]+)$/i) || [, ''])[1],
      hash: a.hash.replace('#', ''),
      path: a.pathname.replace(/^([^\/])/, '/$1'),
      relative: (a.href.match(/tps?:\/\/[^\/]+(.+)/) || [, ''])[1],
      segments: a.pathname.replace(/^\//, '').split('/')
    }
  }
    /**
   ** 除法函数,用来得到精确的除法结果
    ** 说明:javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显。这个函数返回较为精确的除法结果。
    ** 调用:accDiv(arg1,arg2)
    ** 返回值:arg1除以arg2的精确结果
    **/
  accDiv (arg1, arg2, digital = 4) {
    let t1 = 0
    let t2 = 0
    let r1
    let r2
    try {
      t1 = arg1.toString().split('.')[1].length
    } catch (e) {}
    try {
      t2 = arg2.toString().split('.')[1].length
    } catch (e) {}

    r1 = Number(arg1.toString().replace('.', ''))
    r2 = Number(arg2.toString().replace('.', ''))
    return Number((r1 / r2 * Math.pow(10, t2 - t1)).toFixed(digital))
  }
  /**
   ** 减法函数,用来得到精确的减法结果
    ** 说明:javascript的减法结果会有误差,在两个浮点数相减的时候会比较明显。这个函数返回较为精确的减法结果。
    ** 调用:accSub(arg1,arg2)
    ** 返回值:arg1加上arg2的精确结果
    **/
  accSub (arg1, arg2) {
    let r1
    let r2
    let m
    let n
    try {
      r1 = arg1.toString().split('.')[1].length
    } catch (e) {
      r1 = 0
    }
    try {
      r2 = arg2.toString().split('.')[1].length
    } catch (e) {
      r2 = 0
    }
    m = Math.pow(10, Math.max(r1, r2)) // last modify by deeka //动态控制精度长度
    n = r1 >= r2 ? r1 : r2
    return Number(((arg1 * m - arg2 * m) / m).toFixed(n))
  }
  /**
   ** 加法函数,用来得到精确的加法结果
    ** 说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。
    ** 调用:accAdd(arg1,arg2)
    ** 返回值:arg1加上arg2的精确结果
    **/
  accAdd (arg1, arg2) {
    let r1
    let r2
    let m
    let c
    try {
      r1 = arg1.toString().split('.')[1].length
    } catch (e) {
      r1 = 0
    }
    try {
      r2 = arg2.toString().split('.')[1].length
    } catch (e) {
      r2 = 0
    }
    c = Math.abs(r1 - r2)
    m = Math.pow(10, Math.max(r1, r2))
    if (c > 0) {
      let cm = Math.pow(10, c)
      if (r1 > r2) {
        arg1 = Number(arg1.toString().replace('.', ''))
        arg2 = Number(arg2.toString().replace('.', '')) * cm
      } else {
        arg1 = Number(arg1.toString().replace('.', '')) * cm
        arg2 = Number(arg2.toString().replace('.', ''))
      }
    } else {
      arg1 = Number(arg1.toString().replace('.', ''))
      arg2 = Number(arg2.toString().replace('.', ''))
    }
    return (arg1 + arg2) / m
  }
  //把数字 转成汉字数字
  toChineseNum (num) {
    const keys = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']
    var count = ['', '十', '百', '千']
    var str = ''
    var nums = num
      .toString()
      .split('')
      .reverse()
    nums.map(function(a, index) {
      str =
        keys[a] +
        (a == 0 ? '' : count[index > 3 ? index - 4 : index]) +
        (index == '4' ? '万' : '') +
        str
    })
    return str.replace(/(零(?=零))|(零$)|(零(?=万))/g, '')
  }
  // 阿拉伯数字转换为简写汉字
  transformChinese (money) {
    var cnNums = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'] // 汉字的数字
    var cnIntRadice = ['', '拾', '佰', '仟'] // 基本单位
    var cnIntUnits = ['', '万', '亿', '兆'] // 对应整数部分扩展单位
    var cnDecUnits = ['角', '分', '毫', '厘'] // 对应小数部分单位
    // var cnInteger = '整' // 整数金额时后面跟的字符
    var cnIntLast = '元' // 整型完以后的单位
    var maxNum = 999999999999999.9999 // 最大处理的数字
    var IntegerNum // 金额整数部分
    var DecimalNum // 金额小数部分
    var ChineseStr = '' // 输出的中文金额字符串
    var parts // 分离金额后用的数组,预定义
    if (money === '') {
      return ''
    }
    money = parseFloat(money)
    if (money >= maxNum) {
      return ''
    }
    if (money === 0) {
      ChineseStr = cnNums[0] + cnIntLast
      return ChineseStr
    }
    money = money.toString() // 转换为字符串
    if (money.indexOf('.') === -1) {
      IntegerNum = money
      DecimalNum = ''
    } else {
      parts = money.split('.')
      IntegerNum = parts[0]
      DecimalNum = parts[1].substr(0, 4)
    }
    if (parseInt(IntegerNum, 10) > 0) { // 获取整型部分转换
      let zeroCount = 0
      let IntLen = IntegerNum.length
      for (let i = 0; i < IntLen; i++) {
        let n = IntegerNum.substr(i, 1)
        let p = IntLen - i - 1
        let q = p / 4
        let m = p % 4
        if (n === '0') {
          zeroCount++
        } else {
          if (zeroCount > 0) {
            ChineseStr += cnNums[0]
          }
          zeroCount = 0 // 归零
          ChineseStr += cnNums[parseInt(n)] + cnIntRadice[m]
        }
        if (m === 0 && zeroCount < 4) {
          ChineseStr += cnIntUnits[q]
        }
      }
      ChineseStr += cnIntLast
      // 整型部分处理完毕
    }
    if (DecimalNum !== '') { // 小数部分
      let decLen = DecimalNum.length
      for (let i = 0; i < decLen; i++) {
        let n = DecimalNum.substr(i, 1)
        if (n !== '0') {
          ChineseStr += cnNums[Number(n)] + cnDecUnits[i]
        }
      }
    }
    if (ChineseStr === '') {
      ChineseStr += cnNums[0] + cnIntLast
    }
    return ChineseStr
  }

  // 金额大写转换,支持负金额
  digitUppercase (n) {
    var fraction = ['角', '分']
    var digit = [
      '零', '壹', '贰', '叁', '肆',
      '伍', '陆', '柒', '捌', '玖'
    ]
    var unit = [
      ['元', '万', '亿'],
      ['', '拾', '佰', '仟']
    ]
    var head = n < 0 ? '欠' : ''
    n = Math.abs(n)
    var s = ''
    for (var i = 0; i < fraction.length; i++) {
      s += (digit[Math.floor(n * 10 * Math.pow(10, i)) % 10] + fraction[i]).replace(/零./, '')
    }
    s = s || '整'
    n = Math.floor(n)
    for (let i = 0; i < unit[0].length && n > 0; i++) {
      var p = ''
      for (var j = 0; j < unit[1].length && n > 0; j++) {
        p = digit[n % 10] + unit[1][j] + p
        n = Math.floor(n / 10)
      }
      s = p.replace(/(零.)*零$/, '').replace(/^$/, '零') + unit[0][i] + s
    }
    return head + s.replace(/(零.)*零元/, '元')
      .replace(/(零.)+/g, '零')
      .replace(/^整$/, '零元整')
  }
    // 是否有权限
  hasPromission (limit, action) {
    return (limit & action) != 0
  }
  // 移除权限
  deletePromission (limit, action) {
    return limit & ~action
  }
  // 添加权限
  addPromission (limit, action) {
    return limit | action
  }
  // 数组去重
  unique (arr = []) {
    return arr.filter(function(element, index, self) {
      return self.indexOf(element) === index
    })
  }
  // 出现字符最多的
  showMaxString (str = '') {
    let obj = {},  value = 0, name = ''
    for(let i = 0, l = str.length; i < l; i++){
      let k = str.charAt(i)
      let num = 0
      obj[k] ? (num = ++ obj[k]) :(num = obj[k] = 1)
      num > value && (function(){
        name = k
        value = num
      })()
    }
    return {value, name}
  }
  // option = {
  //   open: 'bb://xxx', // 唤起app协议
  //   down: 'https://' // 下载链接
  // }
  TO_NATIVE_APP (elem, option = {}) {
    if (!option.open || !option.down) return
    var IS_IPAD = navigator.userAgent.match(/iPad/i) != null,  
    IS_IPHONE = !IS_IPAD && ((navigator.userAgent.match(/iPhone/i) != null) || (navigator.userAgent.match(/iPod/i) != null)),  
    IS_IOS = IS_IPAD || IS_IPHONE,  
    IS_ANDROID = !IS_IOS && navigator.userAgent.match(/android/i) != null,  
    IS_MOBILE = IS_IOS || IS_ANDROID  
    let iframe, 
        body = document.body, 
        timer = null
    let reg = /MicroMessenger/gi
    iframe = document.createElement('iframe')
    iframe.style.cssText='display:nonewidth=0height=0'
    // 跳转或下载
    elem.addEventListener('click', function() {
      if(reg.test(navigator.userAgent)) {
        // 引导用户在浏览器中打开
        console.log('请在浏览器打开链接')
      } else{
        body.appendChild(iframe)
        iframe.src = option.open
        timer = setTimeout(function() {
          wondow.location.href = url.down
        }, 500)
      }
    }, false)
    // 处理返回浏览器是
    const hanldeEvent = () => {
      clearTimeout(timer)
    }
    document.addEventListener('visibilitychange',hanldeEvent)
    document.addEventListener('webkitvisibilitychange',hanldeEvent)
    window.addEventListener('pagehide',hanldeEvent)
  }
  // 用于遍历数组和对象元素
  each (o, callback = () => false) {
    if (this.isObeject(o) || this.isArray(o)) {
      for (let i in  o) {
        if (callback(o[i], i, o) === false) {
          return
        }
      }
    }
  }
  // 深度克隆 对象
  deepClone (v, o = v) {
    if (this.isDate(v)) {
      o = new Date(v.getTime())
    }
    this.isObeject(v) && (o = {})
    this.isArray(v) && (o = [])
    if (this.isObeject(v) || this.isArray(v)) {
      for (let key in v) {
        o[key] = this.deepClone(v[key])
      }
    }
    return o
  }
  // 生成uuid
  uuid (len, radix) {
  var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('')
  var uuid = []
  let i
  radix = radix || chars.length
  if (len) {
    for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]
  } else {
    var r
    uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'
    uuid[14] = '4'
    for (i = 0; i < 36; i++) {
      if (!uuid[i]) {
        r = 0 | Math.random() * 16
        uuid[i] = chars[(i === 19) ? (r & 0x3) | 0x8 : r]
      }
    }
  }
  return uuid.join('')
}
// 防抖函数,入滚动页面等
debounce(fun, delay) {
  return function (args) {
    let that = this
    clearTimeout(fun.id)
    fun.id = setTimeout(function () {
      fun.call(that, args)
    }, delay)
  }
}
// 节流函数,入连续点及触发
throttle(fun, delay) {
  let last, deferTimer
  return function () {
    let that = this
    let _args = arguments
    let now = +new Date()
    if (last && now < last + delay) {
      clearTimeout(deferTimer)
      deferTimer = setTimeout(function () {
        last = now
        fun.apply(that, _args)
      }, delay)
    }else {
      last = now
      fun.apply(that,_args)
    }
  }
}
// 把扁平结构处理成树结构
  buildTree(list, child = 'id', parent = 'parentId'){
    let temp = {}
    let tree = []
    for(let i in list){
        temp[list[i][child]] = list[i]
    }
    for(let i in temp){
        if(temp[i][parent]) {
            if(!temp[temp[i][parent]].children) {
                temp[temp[i][parent]].children = []
            }
            temp[temp[i][parent]].children.push(temp[i])
        } else {
            tree.push(temp[i])
        }
    }
    return tree
  }
}
var unit = new Units()
export default unit