js实用工具函数总结归纳

lxf2023-04-14 13:36:01

携手创作,共同成长!这是我参与「AdminJS · 8 月更文挑战」的第N天,点击查看活动详情 >>PS:第一天。

1.防抖

      // 防抖
      function debounce (callback,time){
          let timer =null
            return function (e) {
            // this
            // arguments[0] => e
            clearTimeout(timer)
            timer = setTimeout(() => {
              // callback(e) // window.callback()
              // this => ?
              callback.call(this, e) // window.callback()
              // callback.apply(this, arguments)
            }, time)
          }
      }
​
      const oDiv =  document.querySelector('.boxbox')
      oDiv.onmousemove = debounce(function(e){
          let x = e.pageX - this.offsetLeft
          let y = e.pageY - this.offsetTop
          this.innerHTML = `x: ${x}, y: ${y}`
      },500)

2.节流

 // 节流
       function throttle(callback, time) {
          let bBar = true
          return function () {
            if (!bBar) return
            bBar = false
            setTimeout(() => {
              callback.apply(this, arguments)
              bBar = true
            }, time)
          }
        }
      const oDiv =  document.querySelector('.boxbox')
        oDiv.onmousemove = throttle(function (e) {
          const x = e.pageX - this.offsetLeft
          const y = e.pageY - this.offsetTop
          this.innerHTML = `x: ${x}, y: ${y}`
        }, 500)

3.深度拷贝

const obj1 = {
  name: 'ifer',
  age: 18,
  s: Symbol(),
  fn: function () {
    return this
  },
  reg: /\d/,
  un: undefined,
  d: new Date(),
  n: 2n,
}
​
const clone = (target) => {
    // 使用万能检测数据的方法检测数据
  const type = Object.prototype.toString.call(target)
  // #1 日期或正则,'[Object date]' / '[Object regexp]'
  if (/(date|regexp)/i.test(type)) return new target.constructor(target)
  // #2 错误对象,'[Object error]'
  if (/error/i.test(type)) return new target.constructor(target.message)
  // #3 函数,'[Object function]'
  if (/function/i.test(type)) return new Function('return ' + target.toString())()
  // #4 简单数据类型
  if (target === null || typeof target !== 'object') return target
  // #5 数组或对象
  const result = new target.constructor()
  for (let attr in target) {
    result[attr] = clone(target[attr])
  }
  return result
}
​
const obj2 = clone(obj1)
console.log(obj2)

4.树形数据结构转换

  const arr = [
    { id: "a", pid: "", name: "总裁办" },
    { id: "b", pid: "", name: "行政部" },
    { id: "c", pid: "", name: "财务部" },
    { id: "d", pid: "c", name: "财务核算部" },
    { id: "e", pid: "c", name: "税务管理部" },
    { id: "f", pid: "e", name: "税务管理部-分部" },
  ];
// 函数版
function tranListToTreeData(list) {
// 1. 定义两个变量
const treeList = []; const map = {}
​
// 2. 建立一个映射关系,并给每个元素补充children属性.
// 映射关系: 目的是让我们能通过id快速找到对应的元素
// 补充children:让后边的计算更方便
list.forEach(item => {
if (!item.children) {
item.children = []
}
map[item.id] = item
})
​
// 循环
list.forEach(item => {
// 对于每一个元素来说,先找它的上级
//    如果能找到,说明它有上级,则要把它添加到上级的children中去
//    如果找不到,说明它没有上级,直接添加到 treeList
const parent = map[item.pid]
// 如果存在上级则表示item不是最顶层的数据
if (parent) {
parent.children.push(item)
} else {
// 如果不存在上级 则是顶层数据,直接添加
treeList.push(item)
}
})
// 返回
return treeList
}
console.log( tranListToTreeData(arr));

5.获取URL hash后面的参数

export getHashQueryString = (key) => {
  const after = window.location.href.split('?')[1]
  if (after) {
    const reg = new RegExp(`(^|&)${  key  }=([^&]*)(&|$)`)
    const r = after.match(reg)
    if (r != null) {
      return decodeURIComponent(r[2])
    }
    return null
  }
  return null
}

6.判断浏览器是否是移动端

export function isMobile() {
    const agent = navigator.userAgent;
    const k = ["android", "iphone", "ipod", "ipad", "windows phone", "mqqbrowser"];
    let flag = false;
    // Windows
    if (agent.indexOf("Windows NT") < 0 || (agent.indexOf("Windows NT") >= 0 && agent.indexOf("compatible; MSIE 9.0;") >= 0)) {
        // Mac PC
        if (agent.indexOf("Windows NT") < 0 && agent.indexOf("Macintosh") < 0) {
            for (let item of k) {
                if (agent.indexOf(item) >= 0) {
                    flag = true;
                    break;
                }
            }
        }
    }
    return flag;
}

7.判断文件类型

export function checkFileName(fileName, list) {
    if (typeof fileName !== 'string') return;
    let name = fileName.toLowerCase();
    return list.some(i => name.endsWith(`.${i}`) === true)
}
​
export function isImage(fileName) {
    return checkFileName(fileName, ['png', 'jpeg', 'jpg', 'png', 'bmp'])
}
​
export function isH5Video(fileName) {
    return checkFileName(fileName, ['mp4', 'webm', 'ogg'])
}
export function isPdf(fileName) {
    return checkFileName(fileName, ['pdf'])
}
​
export function isWord(fileName) {
    return checkFileName(fileName, ['doc', 'docx'])
}
​
export function isExcel(fileName) {
    return checkFileName(fileName, ['xlsx', 'xls'])
}
​

8.查询元素是否存在某个 class

export function hasClass(el, className) {
  let reg = new RegExp('(^|\s)' + className + '(\s|$)');
  return reg.test(el.className);
}

9.给某个元素添加 class

export function addClass(el, className) {
  if (hasClass(el, className)) {
    return;
  }
  let curClass = el.className.split(' ');
  curClass.push(className);
  el.className = curClass.join(' ');
}

10.图片上传

  1. 准备一个 input type file 框并隐藏。
<input type="file" hidden id="oFile"/>
  1. 点击上传按钮,触发 type file 框的点击事件。
<button id='oBtn'>
    上传
</button>
<script>
oBtn.onclick = function() {
	oFile()
}
</script>
  1. 监听 type file 框的 onchange 事件,通过 e.targetf.files 拿到文件信息。
oFile.onchange = function() {
    e.target.files
}
  1. 创建 FormData 对象,把拿到的文件信息 append 到这个对象。
oFile.onchange = function() {
    const file = e.target.files[0]
    const formData = new FormData()
    formData.append('avatar', file)
    formData.append('name', 'xxx')
}
  1. 使用 axios 把数据传到后端。
oFile.onchange = function() {
    const file = e.target.files[0]
    const formData = new FormData()
    formData.append('avatar', file)
    formData.append('name', 'xxx')
    axios.post('/upload', formData)
}
  1. 前后上传相同的文件信息的时候的一个坑(不会再走 change 里面的业务代码了),解决方式就是每次清空。
oFile.onchange = function() {
    const file = e.target.files[0]
    const formData = new FormData()
    formData.append('avatar', file)
    formData.append('name', 'xxx')
    axios.post('/upload', formData)
    this.value = ''
}

10.图片预览是怎么做的?

  1. 把图片上传到后端,后端返回一个地址,前端把这个地址给图片的 src 属性。
  2. 前端可以把图片信息转成地址去显示。
<input type="file" hidden id="oFile" />
<button id="oBtn">上传</button>
<img src="" alt="" id="oImg" />
<script>
  oBtn.onclick = function () {
    oFile.click()
  }
  /* oFile.onchange = function () {
    // 通过 URL.createObjectURL API 转成 Blob 信息给图片的 src
    oImg.src = URL.createObjectURL(this.files[0])
  } */
  oFile.onchange = function () {
    const reader = new FileReader()
    // 可以把文件信息读成 base64
    reader.readAsDataURL(this.files[0])
    reader.onload = function () {
      oImg.src = this.result
    }
  }
</script>

11.随机生成颜色

export function randomColor() {
  return `rgb(${this.random(0, 255)}, ${this.random(0, 255)}, ${this.random(0, 255)})`
}

12.下载文件

api 接口
params 请求参数
fileName 文件名
const downloadFile = (api, params, fileName, type = 'get') => {
  axios({
    method: type,
    url: api,
    responseType: 'blob', 
    params: params
  }).then((res) => {
    let str = res.headers['content-disposition']
    if (!res || !str) {
      return
    }
    let suffix = ''
    // 截取文件名和文件类型
    if (str.lastIndexOf('.')) {
      fileName ? '' : fileName = decodeURI(str.substring(str.indexOf('=') + 1, str.lastIndexOf('.')))
      suffix = str.substring(str.lastIndexOf('.'), str.length)
    }
    //  如果支持微软的文件下载方式(ie10+浏览器)
    if (window.navigator.msSaveBlob) {
      try {
        const blobObject = new Blob([res.data]);
        window.navigator.msSaveBlob(blobObject, fileName + suffix);
      } catch (e) {
        console.log(e);
      }
    } else {
      //  其他浏览器
      let url = window.URL.createObjectURL(res.data)
      let link = document.createElement('a')
      link.style.display = 'none'
      link.href = url
      link.setAttribute('download', fileName + suffix)
      document.body.appendChild(link)
      link.click()
      document.body.removeChild(link)
      window.URL.revokeObjectURL(link.href);
    }
  }).catch((err) => {
    console.log(err.message);
  })
}`

13.英转中(导出表格)

//手动写一个具有对应关系的对象
      const userRelations = {
        '入职日期': 'timeOfEntry',
        '手机号': 'mobile',
        '姓名': 'username',
        '转正日期': 'correctionTime',
        '工号': 'workNumber'
      }
     //第一个参数是枚举对象,第二个参数是原数组
     function demandArr(userRelations,arr){
      //创建一个空数组
      const newArr = []
      arr.forEach(item => {
      //创建一个空对象
        const obj = {}
        //循环数组里的每一个对象
        for (const k in item) {
          const englishKey = userRelations[k] 
            obj[englishKey] = item[k]
          }
         newArr.push(obj)
       })
      }
  
      // console.log(newArr)

14.中转英(导入表格)

     const data = [
            {
                入职日期: 44505,
                姓名: "小张",
                工号: "9002",
                手机号: "13800000252",
                转正日期: 44892,
                部门: "总裁办"
            },
            {
                入职日期: 44506,
                姓名: "小李",
                工号: 9006,
                手机号: "13810000512",
                转正日期: 44893,
                部门: "总裁办",
            }
        ]
 const transExcel = (results) => {
            const mapInfo = {
                '入职日期': 'timeOfEntry',
                '手机号': 'mobile',
                '姓名': 'username',
                '转正日期': 'correctionTime',
                '工号': 'workNumber',
                '部门': 'departmentName',
            }
            // results.reduce((sum,item) => { 
            //     Object.keys(item)
            //  },{}

            return  results.map(item => {  
               const maps = {}
                Object.keys(item).forEach(value => {
                    maps[mapInfo[value]] = item[value]
                })  
            })
            return maps
        }
        console.log(transExcel(data));

15.路由守卫及页面权限控制

import router, { asyncRoutes } from '@/router'
import store from '@/store'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
// import getPageTitle from '@/utils/get-page-title'

//  to:要去哪个页面
//  from:从哪里来
//  next:它是一个函数。
//     如果直接放行 next()
//  to:要去哪个页面
//  from:从哪里来
//  next:它是一个函数。
//     如果直接放行 next()
//     如果要跳到其它页 next(其它页)
const whiteList = ['/login', '/404']

router.beforeEach(async(to, from, next) => {
  document.title = 'hr人力项目--' + to.meta.title
  //   console.log(to, '跳转至', from)
  // document.title = getPageTitle(to.meta.title)
  NProgress.start() // 开启进度条
  const token = store.state.user.token
  if (token) {
    // 已经登陆
    if (to.path === '/login') {
    // 如果当前在登录页,那么跳转首页
      next('/') // next('/') 只要指定地址跳转,就不会经过router.afterEach(),因此需要手动关闭进度条
      NProgress.done() // 关闭进度条
    } else {
      if (!store.getters.userId) {
      // 即将进入登录页调用获取用户信息的函数 [需满足两个条件,1) 需拥有token   2)并未在登录页上  ]
        const menuList = await store.dispatch('user/getUserInfo')
        console.log(menuList, 'menuListdsadasdsdsa')
        console.log(asyncRoutes, 'asyncRoutes')
        const filterRoutes = asyncRoutes.filter(route => {
          const routeName = route.children[0]
          console.log(route.children[0], 'route.children[0].name')
          return menuList.includes(routeName)
        })
        filterRoutes.push({ path: '*', redirect: '/404', hidden: true })
        // console.log(filterRoutes, 'filterRoutesfilterRoutes')
        router.addRoutes(filterRoutes)
        store.commit('menus/setMenuList', filterRoutes)
        // next({ ...to, replace: true })
        next(to.path)
        // 重新加载页面
      } else {
        next()
      }
      // 如果没有在登录页,那么放行,
    }
  } else {
    // 没有登录
    if (whiteList.includes(to.path)) {
    // 如果此时在白名单页面上,那么放行
      next()
    } else {
      // 如果此时不在白名单页面上,那么跳转至登录页
      next('/login')
      NProgress.done() // 关闭进度条
    }
  }
})
// 页面跳转之后执行钩子函数afterEach()
router.afterEach(() => {
  NProgress.done() // 关闭进度条
})

16.依据后台返回的用户权限进行右侧菜单渲染

        <!-- 菜单区域 -->
        <el-menu
          :default-active="$route.path"
          class="el-menu-vertical-demo"
          background-color="#23262E"
          text-color="#fff"
          active-text-color="#409EFF"
          unique-opened
          router
        >
          <!-- template:模板标签,不会渲染dom结构,起到包裹标签的作用 -->
          <!-- template:模板标签 不能使用:key -->
          <template v-for="(item,index) in menusList">
            <!-- 不包含子菜单的“一级菜单” -->
            <el-menu-item
              :index="item.indexPath"
              :key="item.indexPath"
              v-if="!item.children"
              ><i :class="item.icon"></i>{{ item.title }}</el-menu-item
            >
            <!-- 包含子菜单的“一级菜单” -->
            <el-submenu :index="item.indexPath" :key="index" v-else>
              <template slot="title">
                <i :class="item.icon"></i>
                <span>{{ item.title }}</span>
              </template>
              <el-menu-item
                :index="childItem.indexPath"
                v-for="childItem in item.children"
                :key="childItem.indexPath"
                ><i :class="childItem.icon"></i
                >{{ childItem.title }}</el-menu-item
              >
            </el-submenu>
          </template>
        </el-menu>
// 返回数据
{
    "code": 0,
    "message": "获取左侧菜单成功!",
    "data": [
        {
            "indexPath": "/home",
            "title": "首页",
            "icon": "el-icon-s-home",
            "children": null
        },
        {
            "indexPath": "2",
            "title": "文章管理",
            "icon": "el-icon-s-order",
            "children": [
                {
                    "indexPath": "/art-cate",
                    "title": "文章分类",
                    "icon": "el-icon-s-data"
                },
                {
                    "indexPath": "/art-list",
                    "title": "文章列表",
                    "icon": "el-icon-document-copy"
                }
            ]
        },
        {
            "indexPath": "3",
            "title": "个人中心",
            "icon": "el-icon-user-solid",
            "children": [
                {
                    "indexPath": "/user-info",
                    "title": "基本资料",
                    "icon": "el-icon-s-operation"
                },
                {
                    "indexPath": "/user-avatar",
                    "title": "更换头像",
                    "icon": "el-icon-camera"
                },
                {
                    "indexPath": "/user-pwd",
                    "title": "重置密码",
                    "icon": "el-icon-key"
                }
            ]
        }
    ]
}

17.文件下载

<body>
    <a href="#" download>点击发大财</a>
    <button >点击开豪车</button>
    <script>
        /**
 * 下载文件
 * @param {String} path - 下载地址/下载请求地址。
 * @param {String} name - 下载文件的名字(考虑到兼容性问题,最好加上后缀名)
 */
function downloadFile (path, name) {
    const xhr = new XMLHttpRequest();
    xhr.open('get', path);
    xhr.responseType = 'blob';
    xhr.send();
    xhr.onload = function () {
        if (this.status === 200 || this.status === 304) {
            const fileReader = new FileReader();
            fileReader.readAsDataURL(this.response);
            fileReader.onload = function () {
                const a = document.createElement('a');
                a.style.display = 'none';
                a.href = this.result;
                a.download = name;
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);
            };
        }
    };
}
document.querySelector('a').addEventListener('click',function(){
    console.log(11);
    downloadFile("https://img1.baidu.com/it/u=3096081987,297901132&fm=253&fmt=auto&app=138&f=JPG?w=500&h=375",'img')
})
    </script>

js实用工具函数总结归纳