常用方法

生成指定范围内的随机数

Math.floor(Math.random() * (max - min + 1) + min)

数组去重

// 方法1:利用 es6 的 Set 类型: Set 类型没有重复的数据,结构类似数组
let arr1 = [1, 2, 2, 2, 3]
let arr2 = Array.from(new Set(arr1))
// Array.from 会把类数组对象都转为数组
// 或者:
let arr3 = [...new Set(arr1)]

// 方法2:hash 去重,利用关联数组下标唯一性或对象键的唯一性进行去重
let arr1 = [1, 2, 3, 4, 2, 3, 5, 7, 5, 8]
let arr2 = []
let arr3 = []
for(const element of arr1) {
  if(!arr2[element]) {
    arr2[element] = 1
    arr3.push(element)
  }
}

// 方法3:根据某个字段去重
function arrayUnique (arr, field = 'id') {
  return arr.reduce((all, next) => all.some(item => item[field] === next[field]) ? all : [...all, next], [])
}

深拷贝

// 方法一
const copy = JSON.parse(JSON.stringify(obj))

// 方法二
import { cloneDeep } from 'lodash-es'
const copy = cloneDeep(obj)

// 方法三: 写递归遍历
function clone(o) {
  const temp = {}
  for (const key in o) {
    if (typeof o[key] === 'object') {
      temp[key] = clone(o[key])
    } else {
      temp[key] = o[key]
    }
  }
  return temp
}

判断类型

// 方法一
import { isArray, isFunction, isNull } from 'lodash-es'

// 方法二
function typeOf (obj) {
  const typeMap = {
    '[object Boolean]': 'boolean',
    '[object Number]': 'number',
    '[object String]': 'string',
    '[object Function]': 'function',
    '[object Array]': 'array',
    '[object Date]': 'date',
    '[object RegExp]': 'regExp',
    '[object Undefined]': 'undefined',
    '[object Null]': 'null',
    '[object Object]': 'object',
  }
  return typeMap[Object.prototype.toString.call(obj)]
}

判断两个数据是否相等

// 方法一
import { isEqual } from 'lodash-es'

// 方法二
function equalsObj (oldObj, newObj) {
  if (oldObj === newObj) return true
  if (typeOf(oldObj) !== typeOf(newObj)) return false // typeOf 是自定义函数
  if (typeOf(oldObj) === 'object') {
    if (Object.keys(oldObj).length !== Object.keys(newObj).length) return false
    for(const key in oldObj) {
      if (!newObj.hasOwnProperty(key)) return false
      if (!equalsObj(oldObj[key], newObj[key])) return false
    }
    return true
  } else if (typeOf(oldObj) === 'array') {
    if (oldObj.length !== newObj.length) return false
    for (const i in oldObj) {
      if (!equalsObj(oldObj[i], newObj[i])) return false
    }
    return true
  } else {
    return oldObj.toString() === newObj.toString()
  }
}
isEqual(oldObj, newObj)

节流

规定函数在某时间段内最多执行一次,一般用于鼠标移动、滚动屏幕、轮询加载等场景中,会多次触发 throttle 函数

// 方法一
import { throttle } from 'lodash-es'

// 方法二
// immediately - 首次是否执行
function throttle(fun: (...args)=>void, time = 200, immediately = false) {
  let startTime = Date.now(), donow = immediately;
  return (...args) => {
    const endTime = Date.now();
    if (donow) {
      fun(..arg);
      donow = false;
    }
    if (endTime - startTime >= time) {
      fun(..arg);
      startTime = endTime;
    }
  }
}

防抖

规定函数至少间隔多久执行,函数执行过一次后,在 n 秒内不能再次执行,否则推迟函数执行,一般用于监测鼠标移动结束、滚动屏幕结束、拖拽终止更新等场景中

// 方法一
import { debounce } from 'lodash-es'

// 方法二
function debounce (func, delay = 300) {
    let timer = null
    return (...args) => {
      if (timer) clearTimeout(timer)
      timer = setTimeout(() => {
        func(...args)
      }, delay)
    }
  }

数组转树

npm i array-to-tree

const tree = arrayToTree(dataTwo, {
  parentProperty: 'parent', // 默认 parent_id
  customID: '_id' // 默认 id
});

树转数组

function tree2Array(tree) {
  const temp = []
  const out = []
  temp.push(tree)
  const obj = deepCopy(tree)
  delete obj['children']
  out.push(obj)
  // 广度优先遍历
  while (temp.length) {
    const first = temp.shift()
    const children = first.children
    if(children && children.length) {
      const len = first.children.length
      for (let i=0; i < len; i++){
        temp.push(children[i])
        const obj = deepCopy(children[i])
        delete obj['children']
        out.push(obj)
      }
    }
  }
  return out
}

根据给定的属性路径判断属性是否存在

// eg.: hasOwnProperty(aa, 'b.c.d')  hasOwnProperty(bb, 'e.1.f.0.g')
function hasOwnProperty (obj, key) {
  let tag = true
  const arr = key.split('.')
  arr.reduce((pre, item) => {
    if (pre !== undefined && Object.prototype.hasOwnProperty.call(pre, item)) {
      return pre[item]
    } else {
      tag = false
    }
  }, obj)
  return tag
}

中划线转驼峰

function getCamel (str) {
  return str.replace(/-(\w)/, (_, c) =>  c ? c.toUpperCase() : '');
}

递归

// 根据某一属性值递归查找 obj[key] === value
function findItem(list, key, value) {
  if (Array.isArray(list)) {
    for(const item of list) {
      if (typeof item === 'object' && item[key] === value) {
        return item
      }
      if (item.children && item.children.length) {
        const res = findItem(item.children, key, value)
        if (res) return res
      }
    }
  }
  return null
}

根据字符串获取 query 参数

function getQueryString(key: string){
  const arg: Record<string, string> = {}
  if (location.href.includes('?')){
    const qs = location.href.split('?')
    let query = qs[1]
    if (query.includes('#')){
      // 如果查询字符串后存在hash值,去掉hash值
      query = query.split('#')[0]
    }
    query.split('&').forEach((v: string) => {
      const kv = v.split('=')
      arg[kv[0]] = kv[1]
    })
  }
  return arg[key]
}

js模拟发送按键事件

function dispatchKeyboard(key: string){
  const keyboardEvent = new KeyboardEvent('keydown', {
    code: 'F11',
    keyCode: 122,
    key: 'F11',
  })
  keyboardEvent.initEvent('keydown', true, true)
  document.dispatchEvent(keyboardEvent)
}

判断是否同源

function isSameOrigin(url: string) {
  if (url.startsWith('/')) {
    return true
  }
  const regex = /^(\w+):\/\/([^/:]+)(?::(\d+))?/
  const match = url.match(regex)
  const matchOrigin = location.href.match(regex)
  if (match && matchOrigin) {
    let [, protocol1, host1, port1] = match
    let [, protocol2, host2, port2] = matchOrigin
    if (port1) {
      port1 = '80'
    }
    if (port2) {
      port2 = '80'
    }
    return protocol1 === protocol2 && host1 === host2 && port1 === port2
  }
  return false
}

切换全屏

function toggleFullscreen() {
  const element = document.documentElement
  if(element.fullscreenElement) {
    document.exitFullscreen && document.exitFullscreen()
  } else {
    element.requestFullscreen && element.requestFullscreen()
  }
}