日常开发常用工具函数汇总

1. 简单数组去重

  • 第一种方式:

    使用 reduce 函数返回值是累加的结果,判断当前遍历值是否在其中。

function removeDuplicates(arr) {
  let result = arr.reduce(function (init, current) {
    if (init.indexOf(current) === -1) {
      init.push(current);
    }
    return init;
  }, []);
  return result;
}
let myArray = ["a", "b", "a", "b", "c", "e", "e", "c", "d", "d", "d", "d"];
console.log(removeDuplicates(myArray)); //  ["a", "b", "c", "e", "d"]
  • 第二种方式:

    先使用排序,把相同的元素放到一起,然后再使用 reduce 方法让当前遍历对象跟前一个进行比较,相同就不加入,不同则加入。

function removeDuplicates(arr) {
  let result = arr.sort().reduce((init, current) => {
    if (init.length === 0 || init[init.length - 1] !== current) {
      init.push(current);
    }
    return init;
  }, []);
  return result;
}
let arr = [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4];
console.log(removeDuplicates(arr)); //[1,2,3,4,5]
  • 第三种方式:

    利用 indexOf 总是返回该项的第一个出现的索引,所以我们可以判断当前在过滤循环中的项是否是重复的。如果是,我们就不返回到由 filter()方法创建的新数组中。

function removeDuplicates(arr) {
  let result = arr.filter((item, index) => {
    return arr.indexOf(item) === index;
  });
  return result;
}
let arr = [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4];
console.log(removeDuplicates(arr)); //[1,2,3,4,5]

2. 数组中对象去重

function removeDuplicates(arr) {
  const tempObj = {};
  let result = arr.reduce((init, current) => {
    tempObj[current.name]
      ? ""
      : (tempObj[current.name] = true && init.push(current));
    return init;
  }, []);
  return result;
}
// 注意: name字段必须为current中存在的字段

3. 扁平化嵌套数组

  • 方式一:

    使用 flat 方法,该方法接受一个可选参数,几维数组展开就把数字几作为参数,使用 Infinity 作为参数时可展开任意深度的嵌套数组

function flatArray(arr) {
  return arr.flat();
}
  • 方法二:

    使用 reduce 方法,该方法仅适用于二维数组

function flatArray(arr) {
  return arr.reduce((init, current) => init.concat(current), []);
}

4. 将数组转化为树形结构

  • 将如下数据转化为树状结构
let arr = [
  {
    id: 1,
    name: "1",
    pid: 0,
  },
  {
    id: 2,
    name: "1-1",
    pid: 1,
  },
  {
    id: 3,
    name: "1-1-1",
    pid: 2,
  },
];
  • 实现方式
function toTree(data, parentId = 0) {
  var itemArr = [];
  for (var i = 0; i < data.length; i++) {
    var node = data[i];
    if (node.pid === parentId) {
      var newNode = {
        ...node,
        name: node.name,
        id: node.id,
        children: toTree(data, node.id),
      };
      itemArr.push(newNode);
    }
  }
  return itemArr;
}

5. 格式化时间

  • 方式一:
const getDate = (timeStamp, format, type) => {
  const d = new Date(timeStamp || 0);
  const year = d.getFullYear();
  const month = getHandledValue(d.getMonth() + 1);
  const day = getHandledValue(d.getDate());
  const hour = getHandledValue(d.getHours());
  const minute = getHandledValue(d.getMinutes());
  const second = getHandledValue(d.getSeconds());
  let resStr = "";
  if (!type) {
    type = "/";
  }
  switch (format) {
    case "yyyymmdd":
      return [year, month, day].map(formatNumber).join(type);
    case "yyyymm":
      return [year, month].map(formatNumber).join(type);
    case "mmdd":
      return [month, day].map(formatNumber).join(type);
    case "yyyy":
      return year;
    case "mm":
      return [month].map(formatNumber);
    case "dd":
      return [day].map(formatNumber);
    case "yyyymmddhhmmss":
      return (
        [year, month, day].map(formatNumber).join(type) +
        " " +
        [hour, minute, second].map(formatNumber).join(":")
      );
    case "yyyymmddhhmm":
      return (
        [year, month, day].map(formatNumber).join(type) +
        " " +
        [hour, minute].map(formatNumber).join(":")
      );
    case "hhmmss":
      return [hour, minute, second].map(formatNumber).join(":");
    case "hhmm":
      return [hour, minute].map(formatNumber).join(":");
    case "hh":
      return [hour].map(formatNumber);
    case "mi":
      return [minute].map(formatNumber);
    default:
      return (
        [year, month, day].map(formatNumber).join("/") +
        " " +
        [hour, minute, second].map(formatNumber).join(":")
      );
  }
};
  • 方式二:
const formatTime = (date) => {
  let fmt = 'yyyy-MM-dd hh:mm:ss'
  const o = {
    'M+': date.getMonth() + 1, // 月份
    'd+': date.getDate(), // 日
    'h+': date.getHours(), // 小时
    'm+': date.getMinutes(), // 分钟
    's+': date.getSeconds(), // 秒
  }

  if (/(y+)/.test(fmt)) {
    fmt = fmt.replace(RegExp.$1, date.getFullYear())
  }
  for (let k in o) {
    if (new RegExp('(' + k + ')').test(fmt)) {
      fmt = fmt.replace(RegExp.$1, o[k].toString().length == 1 ? '0' + o[k] : o[k])
    }
  }
  return fmt
}

6. 获取相对时间

/**
 * @param {String|Number} timeStamp 时间戳
 * @returns {String} 相对时间字符串
 */
const getRelativeTime = (timeStamp) => {
  // 判断当前传入的时间戳是秒格式还是毫秒
  const IS_MILLISECOND = isMillisecond(timeStamp);
  // 如果是毫秒格式则转为秒格式
  if (IS_MILLISECOND) Math.floor((timeStamp /= 1000));
  // 传入的时间戳可以是数值或字符串类型,这里统一转为数值类型
  timeStamp = Number(timeStamp);
  // 获取当前时间时间戳
  const currentTime = Math.floor(Date.parse(new Date()) / 1000);
  // 判断传入时间戳是否早于当前时间戳
  const IS_EARLY = isEarly(timeStamp, currentTime);
  // 获取两个时间戳差值
  let diff = currentTime - timeStamp;
  // 如果IS_EARLY为false则差值取反
  if (!IS_EARLY) diff = -diff;
  let resStr = "";
  const dirStr = IS_EARLY ? "前" : "后";
  // 少于等于59秒
  if (diff <= 59) resStr = diff + "秒" + dirStr;
  // 多于59秒,少于等于59分钟59秒
  else if (diff > 59 && diff <= 3599)
    resStr = Math.floor(diff / 60) + "分钟" + dirStr;
  // 多于59分钟59秒,少于等于23小时59分钟59秒
  else if (diff > 3599 && diff <= 86399)
    resStr = Math.floor(diff / 3600) + "小时" + dirStr;
  // 多于23小时59分钟59秒,少于等于29天59分钟59秒
  else if (diff > 86399 && diff <= 2623859)
    resStr = Math.floor(diff / 86400) + "天" + dirStr;
  // 多于29天59分钟59秒,少于364天23小时59分钟59秒,且传入的时间戳早于当前
  else if (diff > 2623859 && diff <= 31567859 && IS_EARLY)
    resStr = getDate(timeStamp);
  else resStr = getDate(timeStamp, "year");
  return resStr;
};

7. 获取当前浏览器

/**
 * @returns {String} 当前浏览器名称
 */
const getExplorer = () => {
  const ua = window.navigator.userAgent;
  const isExplorer = (exp) => {
    return ua.indexOf(exp) > -1;
  };
  if (isExplorer("MSIE")) return "IE";
  else if (isExplorer("Firefox")) return "Firefox";
  else if (isExplorer("Chrome")) return "Chrome";
  else if (isExplorer("Opera")) return "Opera";
  else if (isExplorer("Safari")) return "Safari";
};

8. 判断两个对象是否相等

/**
 * @param {*} obj1 对象
 * @param {*} obj2 对象
 * @description 判断两个对象是否相等,这两个对象的值只能是数字或字符串
 */
const objEqual = (obj1, obj2) => {
  const keysArr1 = Object.keys(obj1);
  const keysArr2 = Object.keys(obj2);
  if (keysArr1.length !== keysArr2.length) return false;
  else if (keysArr1.length === 0 && keysArr2.length === 0) return true;
  /* eslint-disable-next-line */ else
    return !keysArr1.some((key) => obj1[key] != obj2[key]);
};

9. 百分比转小数

const percentageToDecimal = (num) => {
  if (typeof num !== "number") num = Number(num);
  let convert = num;
  let unit = 100;
  return convert / unit;
};

10. 小数转百分比

const decimalToPercentage = (num) => {
  if (num && typeof num === "number") {
    let convert = num;
    let unit = 100;
    return convert * unit;
  } else {
    return num;
  }
};

11. 数组按指定份数分割

const splitArr = (data, senArrLen) => {
  //处理成len个一组的数据
  let data_len = data.length;
  let arrOuter_len =
    data_len % senArrLen === 0
      ? data_len / senArrLen
      : parseInt(data_len / senArrLen + "") + 1;
  let arrSec_len = data_len > senArrLen ? senArrLen : data_len; //内层数组的长度
  let arrOuter = new Array(arrOuter_len); //最外层数组
  let arrOuter_index = 0; //外层数组的子元素下标
  for (let i = 0; i < data_len; i++) {
    if (i % senArrLen === 0) {
      arrOuter_index++;
      let len = arrSec_len * arrOuter_index;
      //将内层数组的长度最小取决于数据长度对len取余,平时最内层由下面赋值决定
      arrOuter[arrOuter_index - 1] = new Array(data_len % senArrLen);
      if (arrOuter_index === arrOuter_len)
        //最后一组
        data_len % senArrLen === 0
          ? (len = (data_len % senArrLen) + senArrLen * arrOuter_index)
          : (len = (data_len % senArrLen) + senArrLen * (arrOuter_index - 1));
      let arrSec_index = 0; //第二层数组的索引
      for (let k = i; k < len; k++) {
        //第一层数组的开始取决于第二层数组长度*当前第一层的索引
        arrOuter[arrOuter_index - 1][arrSec_index] = data[k];
        arrSec_index++;
      }
    }
  }
  return arrOuter;
};

12. 下载图片

const downloadImg = (imgsrc, name) => {
  //下载图片地址和图片名
  let image = new Image();
  // 解决跨域 Canvas 污染问题
  image.setAttribute("crossOrigin", "anonymous");
  image.onload = function () {
    let canvas = document.createElement("canvas");
    canvas.width = image.width;
    canvas.height = image.height;
    let context = canvas.getContext("2d");
    context.drawImage(image, 0, 0, image.width, image.height);
    let url = canvas.toDataURL("image/png"); //得到图片的base64编码数据
    let a = document.createElement("a"); // 生成一个a元素
    let event = new MouseEvent("click"); // 创建一个单击事件
    a.download = name || "photo"; // 设置图片名称
    a.href = url; // 将生成的URL设置为a.href属性
    a.dispatchEvent(event); // 触发a的单击事件
  };
  image.src = imgsrc;
};

13. 拖拽移动

export const dragToMove = (dragDom, dragId) => {
  var startEvt, moveEvt, endEvt;
  // 判断是否支持触摸事件
  if ("ontouchstart" in window) {
    startEvt = "touchstart";
    moveEvt = "touchmove";
    endEvt = "touchend";
  } else {
    startEvt = "mousedown";
    moveEvt = "mousemove";
    endEvt = "mouseup";
  }
  // 获取元素
  var drag = dragDom;
  var disX, disY, left, top, starX, starY;
  var firstTime = "",
    lastTime = "";
  drag.addEventListener(startEvt, function (e) {
    // 阻止页面的滚动,缩放
    e.preventDefault();
    document.getElementById(dragId).setAttribute("data-flag", false);
    firstTime = new Date().getTime();
    // 兼容IE浏览器
    var e = e || window.event;
    // 手指按下时的坐标
    starX = e.touches ? e.touches[0].clientX : e.clientX;
    starY = e.touches ? e.touches[0].clientY : e.clientY;
    // 手指相对于拖动元素左上角的位置
    disX = starX - drag.offsetLeft;
    disY = starY - drag.offsetTop;
    document.addEventListener(moveEvt, moveFun);
    document.addEventListener(endEvt, endFun);
  });

  function moveFun(e) {
    // 兼容IE浏览器
    var e = e || window.event;
    left = (e.touches ? e.touches[0].clientX : e.clientX) - disX;
    top = (e.touches ? e.touches[0].clientY : e.clientY) - disY;
    // 限制拖拽的X范围,不能拖出屏幕
    if (left < 0) {
      left = 0;
    } else if (left > document.documentElement.clientWidth - drag.offsetWidth) {
      left = document.documentElement.clientWidth - drag.offsetWidth;
    }
    // 限制拖拽的Y范围,不能拖出屏幕
    if (top < 0) {
      top = 0;
    } else if (
      top >
      document.documentElement.clientHeight - drag.offsetHeight
    ) {
      top = document.documentElement.clientHeight - drag.offsetHeight;
    }
    drag.style.left = left + "px";
    drag.style.top = top + "px";
  }

  function endFun(e) {
    document.removeEventListener(moveEvt, moveFun);
    document.removeEventListener(endEvt, endFun);
    lastTime = new Date().getTime();
    if (lastTime - firstTime < 200) {
      document.getElementById(dragId).setAttribute("data-flag", true);
      if (endEvt == "touchend") {
        document.getElementById(dragId).click();
      }
    }
  }
};

14. 从 url 中解析出参数

/**
 * @param {String} url
 * @description 从URL中解析参数
 */
const getParams = (url) => {
  const keyValueArr = url.split("?")[1].split("&");
  let paramObj = {};
  keyValueArr.forEach((item) => {
    const keyValue = item.split("=");
    paramObj[keyValue[0]] = keyValue[1];
  });
  return paramObj;
};

15. 格式化金额

const formatNumber = (val = 0) => {
  isNaN(val) ? (val = 0) : val;
  return (val * 1).toLocaleString("zh", { minimumFractionDigits: 2 });
};

16. 加减乘法

// 减法#
const subtr = (arg1, arg2) => {
  let num1 = arg1 || 0;
  let num2 = arg2 || 0;
  let r1, r2, m, n;
  try {
    r1 = num1.toString().split(".")[1].length;
  } catch (e) {
    r1 = 0;
  }
  try {
    r2 = num2.toString().split(".")[1].length;
  } catch (e) {
    r2 = 0;
  }
  m = Math.pow(10, Math.max(r1, r2));
  n = r1 >= r2 ? r1 : r2;
  return ((mul(num1, m) - mul(num2, m)) / m).toFixed(n);
};
//乘法
const mul = (arg1, arg2) => {
  let m = 0,
    s1 = arg1.toString(),
    s2 = arg2.toString();
  try {
    m += s1.split(".")[1].length;
  } catch (e) {}
  try {
    m += s2.split(".")[1].length;
  } catch (e) {}
  return (
    (Number(s1.replace(".", "")) * Number(s2.replace(".", ""))) /
    Math.pow(10, m)
  );
};
//加法
const accAdd = (arg1, arg2) => {
  let num1 = arg1 || 0;
  let num2 = arg2 || 0;
  let r1, r2, m;
  try {
    r1 = num1.toString().split(".")[1].length;
  } catch (e) {
    r1 = 0;
  }
  try {
    r2 = num2.toString().split(".")[1].length;
  } catch (e) {
    r2 = 0;
  }
  m = Math.pow(10, Math.max(r1, r2));
  return (mul(num1, m) + mul(num2, m)) / m;
};

17. 自定义指令-按钮权限控制

Vue.directive("has", {
  inserted: function (el, binding) {
    if (!Vue.prototype.$has(binding.value)) {
      el.parentNode.removeChild(el);
    }
  },
});

18. 自定义指令-防快速重复点击按钮

import Vue from "vue";
Vue.directive("preventReClick", {
  inserted(el) {
    el.addEventListener("click", () => {
      if (!el.disabled) {
        el.disabled = true;
        setTimeout(() => {
          el.disabled = false;
        }, 2000);
      }
    });
  },
});

19. 获取时间差

const getDateDiff = (startTime, endTime, diffType) => {
  // 将xxxx-xx-xx的时间格式,转换为 xxxx/xx/xx的格式
  startTime = startTime.replace(/-/g, "/");
  endTime = endTime.replace(/-/g, "/");

  // 将计算间隔类性字符转换为小写
  diffType = diffType.toLowerCase();
  var sTime = new Date(startTime); // 开始时间
  var eTime = new Date(endTime); // 结束时间
  // 作为除数的数字
  var divNum = 1;
  switch (diffType) {
    case "second":
      divNum = 1000;
      break;
    case "minute":
      divNum = 1000 * 60;
      break;
    case "hour":
      divNum = 1000 * 3600;
      break;
    case "day":
      divNum = 1000 * 3600 * 24;
      break;
    default:
      break;
  }
  return parseInt((eTime.getTime() - sTime.getTime()) / parseInt(divNum));
};

20. 获取文件扩展名

const getFileType = (filePath) => {
  var startIndex = filePath.lastIndexOf(".");
  if (startIndex !== -1) {
    return filePath.substring(startIndex + 1, filePath.length).toLowerCase();
  } else {
    return "";
  }
};

21. 判断数组中是否有重复对象

const isRepeat = (arr) => {
  const obj = {};
  let flag = false;
  arr.map((item) => {
    obj[item.id] ? (flag = true) : (obj[item.id] = true);
  });
  return flag;
};

22. 压缩图片

export function compress(fileObj) {
  return new Promise(resolve => {
    function dataURLtoBlob(dataUrl) {
      //base64格式图片 转为Blob
      const arr = dataUrl.split(','),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1])
      let n = bstr.length
      const u8arr = new Uint8Array(n)
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n)
      }
      return new File([u8arr], fileObj.name, {
        lastModified: fileObj.lastModifiedDate,
        type: mime
      })
    }
    if (typeof FileReader === 'undefined') {
      console.log('当前浏览器内核不支持base64图标压缩')
      resolve(fileObj)
    }
    try {
      const reader = new FileReader()
      const image = new Image()
      reader.readAsDataURL(fileObj) //开始读取指定的Blob中的内容。返回base64
      reader.onload = function (ev) {
        image.src = String(ev.target.result)
        image.onload = function () {
          let imgWidth = image.width,
            imgHeight = image.height //获取图片宽高
          //设置图片的最大宽度为300
          imgWidth = image.width
          imgHeight = image.height //设置等比例高度
          const canvas = document.createElement('canvas')
          const ctx = canvas.getContext('2d')
          canvas.width = imgWidth
          canvas.height = imgHeight
          ctx.drawImage(image, 0, 0, imgWidth, imgHeight) //根据宽高绘制图片
          const fullQuality = canvas.toDataURL('image/jpeg', 0.05) //canvas转为base64
          const blogData = dataURLtoBlob(fullQuality)
          resolve(blogData)
        }
      }
    } catch (e) {
      resolve(fileObj)
    }
  })
}

23. 大数字转换为万/亿

/*
* @params : num 数字
* @params : point 保留几位小数
*/
export function transferNumber(num, point) {
      let numStr = num.toString().split('.')[0]
      if (numStr.length < 6) { // 十万以下不转化原数字显示
        return numStr
      } else if (numStr.length >= 6 && numStr.length <= 8) {
        // 6-8位:十万 - 千万 转化为万
        let decimal = numStr.substring(numStr.length - 4, numStr.length - 4 + point)
        return parseFloat(parseInt(num / 10000) + '.' + decimal) +
          '万'
      } else if (numStr.length > 8) {
        // 超过8为 显示亿
        let decimal = numStr.substring(numStr.length - 8, numStr.length - 8 + point)
        return parseFloat(parseInt(num / 100000000) + '.' + decimal) + '亿'
      }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容