工作中常用JS方法总结

208 阅读9分钟

用户权限

用户是否有权限跳转某个页面

  1. 用户信息中包含用户权限数组,如:['super_admin', 'admin'] --- access
  2. 用户点击跳转,获取要跳转的路径 --- name
  3. 后台返回路由列表 --- routes
  4. 根据 name 在routes中查找 name 对应的权限数组 --- item.name
  5. 根据当前用户的权限数组access,去匹配路由列表中 name 对应的权限列表
  6. 如果有,则该用户有权限跳转。否则,无权限跳转。
// utils.js
/**
 * 权鉴
 * @param {*} name 即将跳转的路由name
 * @param {*} access 用户权限数组
 * @param {*} routes 路由列表
 * @description 用户是否可跳转到该页
 */
export const canTurnTo = (name, access, routes) => {
  const routePermissionJudge = (list) => {
    return list.some((item) => {
      if (item.children && item.children.length) {
        return routePermissionJudge(item.children);
      } else if (item.name === name) {
        return hasAccess(access, item);
      }
    });
  };

  return routePermissionJudge(routes);
};

/**
 * @param {*} access 用户权限数组,如 ['super_admin', 'admin']
 * @param {*} route 路由列表
 */
const hasAccess = (access, route) => {
  if (route.meta && route.meta.access) {
    return hasOneOf(access, route.meta.access);
  } else return true;
};

// tools.js
/**
 * @param {Array} target 目标数组
 * @param {Array} arr 需要查询的数组
 * @description 判断要查询的数组是否至少有一个元素包含在目标数组中
 */
export const hasOneOf = (targetarr, arr) => {
  return targetarr.some(_ => arr.indexOf(_) > -1);
};

cookies 函数

import Cookies from 'js-cookie';
// cookie保存的天数
import config from '@/config';
const { cookieExpires } = config;

export const TOKEN_KEY = 'token';

export const setToken = (token) => {
  Cookies.set(TOKEN_KEY, token, { expires: cookieExpires || 1 });
};

export const getToken = () => {
  const token = Cookies.get(TOKEN_KEY);
  if (token) return token;
  else return false;
};

export const removeToken = () => {
  Cookies.remove(TOKEN_KEY);
};

从 url 中解析参数

/**
 * @param {String} url
 * @description 从URL中解析参数
 */
export 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;
};

从 csv 中解析数据

/**
 * @param {Object} file 从上传组件得到的文件对象
 * @returns {Promise} resolve参数是解析后的二维数组
 * @description 从Csv文件中解析出表格,解析成二维数组
 */
export const getArrayFromFile = (file) => {
  let nameSplit = file.name.split('.');
  let format = nameSplit[nameSplit.length - 1];
  return new Promise((resolve, reject) => {
    let reader = new FileReader();
    reader.readAsText(file); // 以文本格式读取
    let arr = [];
    reader.onload = function(evt) {
      let data = evt.target.result; // 读到的数据
      let pasteData = data.trim();
      arr = pasteData
        .split(/[\n\u0085\u2028\u2029]|\r\n?/g)
        .map((row) => {
          return row.split('\t');
        })
        .map((item) => {
          return item[0].split(',');
        });
      if (format === 'csv') resolve(arr);
      else reject(new Error('[Format Error]:你上传的不是Csv文件'));
    };
  });
};

事件绑定与解绑

/**
 * @description 绑定事件 on(element, event, handler)
 */
export const on = (function() {
  if (document.addEventListener) {
    return function(element, event, handler) {
      if (element && event && handler) {
        element.addEventListener(event, handler, false);
      }
    };
  } else {
    return function(element, event, handler) {
      if (element && event && handler) {
        element.attachEvent('on' + event, handler);
      }
    };
  }
})();

/**
 * @description 解绑事件 off(element, event, handler)
 */
export const off = (function() {
  if (document.removeEventListener) {
    return function(element, event, handler) {
      if (element && event) {
        element.removeEventListener(event, handler, false);
      }
    };
  } else {
    return function(element, event, handler) {
      if (element && event) {
        element.detachEvent('on' + event, handler);
      }
    };
  }
})();

获取浏览器名称

/**
 * @returns {String} 当前浏览器名称
 */
export 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';
};

删除对象中为空的属性

过滤对象中为 null、undefined、''、[]、{} 的属性值 --- 改变原对象

/**
 * @param {obj} 需要修改的对象数据
 * @description 删除obj中为空的属性
 */
function clearDeep(obj) {
    if (!obj || !typeof obj === 'object') return
 
    const keys = Object.keys(obj)
    for (var key of keys) {
      const val = obj[key]
      if (
        typeof val === 'undefined' ||
        ((typeof val === 'object' || typeof val === 'string') && !val)
      ) {
        // 如属性值为null或undefined或'',则将该属性删除
        delete obj[key]
      } else if (typeof val === 'object') {
        // 属性值为对象,递归调用
        clearDeep(obj[key])
 
        if (Object.keys(obj[key]).length === 0) {
          // 如某属性的值为不包含任何属性的独享,则将该属性删除
          delete obj[key]
        }
      }
    }
}

测试

var obj3 = [
    {
      'a': 1,
      'test': {}
    },
    {
      'b': ''
    },
    {
      'c': '',
      'd': {
        'e': null,
        'f': undefined,
        'g': 'test',
        'h': [
          {
            'i': '',
            'j': '哈哈哈',
            'k': []
          }
        ]
      }
    }
]
clearDeep(obj3)
console.log(obj3)

树形图查询方法

根据输入框中输入的关键字,进行查询操作

/**
 * @param keyword: string 查询关键字
 * @param arrData:[] 需要查询的数组
 * @param cate: string 查询字段
 * @returns [] 返回根据关键字查询到的数组
 * @description 根据输入框中输入的关键字,进行查询操作
 */
function searchByKeyword(keyword, list, cate) {
    // 进行深拷贝 --- 查询操作会更改原数组 list
    let arrData = JSON.parse(JSON.stringify(list));
    let searchedData = []
    if (keyword) {
        // 如果用户输入内容,执行查询操作
        searchedData = arrData.filter(item => {
          if (item[cate] && item[cate].toLowerCase().includes(keyword.toLowerCase())) {
            return true;
          }else {
              if(item.children && item.children.length > 0) {
                let children = this.searchByKeyword(keyword.toLowerCase(), item.children, cate);
                item.children = children;
              }
              let flag = item.children && item.children.length>0 ? true : false;
              return flag;
          }
        });
    }else {
      searchedData = initDbData;
    }
    return searchedData
  }

常用的正则表达式

//(1)匹配 16 进制颜色值
var color = /#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g;

//(2)匹配日期,如 yyyy-mm-dd 格式
var date = /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/;

//(3)匹配 qq 号
var qq = /^[1-9][0-9]{4,10}$/g;

//(4)手机号码正则
var phone = /^1[34578]\d{9}$/g;

//(5)用户名正则
var username = /^[a-zA-Z\$][a-zA-Z0-9_\$]{4,16}$/;

//(6)Email正则
var email = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;

//(7)身份证号(18位)正则
var cP = /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;

//(8)URL正则
var urlP= /^((https?|ftp|file):\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/;

// (9)ipv4地址正则
var ipP = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;

// (10)//车牌号正则
var cPattern = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$/;

// (11)强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间):var pwd = /^(?=.\d)(?=.[a-z])(?=.[A-Z]).{8,10}$/

数组

数组扁平化

将数组扁平化并去除其中重复数据,最终得到一个升序且不重复的数组

const arr = [[1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
// 方法一:
function flatArr(arr) {
  return arr.reduce((pre, item)=>{
  	if(!Array.isArray(item)){
    	pre.push(item)
    }else{
    	pre = pre.concat(flatArr(item))
    }
    return [...new Set(pre.sort((a, b) => a - b))]
  }, [])
}
console.log(flatArr(arr))

// 方法二:
function flatArr1(arr) {
  let newArr = arr.reduce((pre, item) => !Array.isArray(item)?pre.concat(item):pre.concat(flatArr1(item)), [])
  return [...new Set(newArr.sort((a, b) => a - b))]
}

console.log(flatArr1(arr))

// 方法三:
// 使用Set方法去重,flat(Infinity)扁平化
Array.from(newSet(arr.flat(Infinity))).sort((a,b)=>{returna-b})
//[1,2,3,4,5,6,7,8,9,10,11,12,13,14]

数组交集

普通对象

const arr1 = [1, 2, 3, 4, 5 , 8 ,9],arr2 = [5, 6, 7, 8, 9];

function intersection(arr1, arr2) {
  return arr1.filter((val) => { 
    return arr2.indexOf(val) > -1
  })
}
console.log(intersection(arr2, arr1)) //[5, 8, 9]

数组对象

数组对象目前仅针对value值为简单的Number,String,Boolan数据类型

const arr1 = [{ name: 'name1', id: 1 }, { name: 'name2', id: 2 }, { name: 'name3', id: 3 }, { name: 'name5', id: 5 }];
const arr2 = [{ name: 'name1', id: 1 }, { name: 'name2', id: 2 }, { name: 'name3', id: 3 }, { name: 'name4', id: 4 }, { name: 'name5', id: 5 }];
function result(arr1, arr2) {
  return arr2.filter(function (v) {
    return arr1.some(n => JSON.stringify(n) === JSON.stringify(v))
  })
}
console.log(result(arr1, arr2)); // [{ name: 'name1', id: 1 },{ name: 'name2', id: 2 },{ name: 'name3', id: 3 },{ name: 'name5', id: 5 }]

数组并集

普通数组

const arr1 = [1, 2, 3, 4, 5, 8, 9]
const arr2 = [5, 6, 7, 8, 9];
function union(arr1, arr2) {
  return arr1.concat(arr2.filter(v => !arr1.includes(v)))
}
console.log(union(arr1, arr2)) //[1, 2, 3, 4, 5, 8, 9, 6, 7]

数组对象

const arr1 = [{ name: 'name1', id: 1 }, { name: 'name2', id: 2 }, { name: 'name3', id: 3 }];
const arr2 = [{ name: 'name1', id: 1 }, { name: 'name4', id: 4 }, { name: 'name5', id: 5 }];

function unionObj(arr1, arr2) {
  let arr3 = arr1.concat(arr2);
  let result = [];
  let obj = [];
  return arr3.reduce(function (prev, cur, index, arr) {
    obj[cur.id] ? '' : obj[cur.id] = true && prev.push(cur);
    return prev;
  }, []);
}
console.log(unionObj(arr1, arr2)); 
//[{ name: 'name1', id: 1 },{ name: 'name2', id: 2 },{ name: 'name3', id: 3 },{ name: 'name4', id: 4 },{ name: 'name5', id: 5 }]

数组差集

数组arr1相对于arr2所没有的

普通数组

const arr1 = [1, 2, 3, 4, 5, 8, 9]
const arr2 = [5, 6, 7, 8, 9];
function difference(arr1, arr2) {
  return arr1.filter(item => !new Set(arr2).has(item))
}
console.log(difference(arr1, arr2)) //[ 1, 2, 3, 4 ]

数组对象

let arr1 = [{ name: 'name1', id: 1 }, { name: 'name2', id: 2 }, { name: 'name3', id: 3 }];
let arr2 = [{ name: 'name1', id: 1 }, { name: 'name4', id: 4 }, { name: 'name5', id: 5 }];

function diffObj(arr1, arr2) {
  return arr1.filter(function (v) {
    return arr2.every(n => JSON.stringify(n) !== JSON.stringify(v))
  })
}
console.log(diffObj(arr1, arr2)); 

数组补集

两个数组各自没有的集合

普通数组

const arr1 = [1, 2, 3, 4, 5, 8, 9]
const arr2 = [5, 6, 7, 8, 9];
function complement(arr1, arr2)  {
  return Array.from(new Set(arr1.concat(arr2).filter(v => !new Set(arr1).has(v) || !new Set(arr2).has(v)))) 
}
console.log(complement(arr1, arr2)) //[ 1, 2, 3, 4, 6, 7 ]

数组对象

let arr1 = [{ name: 'name1', id: 1 }, { name: 'name2', id: 2 }, { name: 'name3', id: 3 }];
let arr2 = [{ name: 'name1', id: 1 }, { name: 'name4', id: 4 }, { name: 'name5', id: 5 }];

function complementObj(arr1, arr2)  {
  let arr3 = arr1.concat(arr2);
  return arr3.filter(function (v) {
    return arr1.every(n => JSON.stringify(n) !== JSON.stringify(v)) || arr2.every(n => JSON.stringify(n) !== JSON.stringify(v))
  })
}
console.log(complementObj(arr1, arr2) ); 
// [{ name: 'name2', id: 2 },{ name: 'name3', id: 3 },{ name: 'name4', id: 4 },{ name: 'name5', id: 5 }]

数组去重

普通数组

console.log(Array.from(new Set([1, 2, 3, 3, 4, 4]))) //[1,2,3,4]
console.log([...new Set([1, 2, 3, 3, 4, 4])]) //[1,2,3,4]

数组对象

const arr = [{ name: 'name1', id: 1 }, { name: 'name2', id: 2 }, { name: 'name3', id: 3 }, { name: 'name1', id: 1 }, { name: 'name4', id: 4 }, { name: 'name5', id: 5 }];

 function duplicate(arr) {
  const result = [];
  arr.forEach(item=>{
     !result.some(v => JSON.stringify(v) === JSON.stringify(item)) && result.push(item)
  })
  return result
 }
 console.log(duplicate(arr)) //[{ name: 'name1', id: 1 },{ name: 'name2', id: 2 },{ name: 'name3', id: 3 },{ name: 'name4', id: 4 },{ name: 'name5', id: 5 }]

最大值

普通数组

Math.max(...[1, 2, 3, 4]) //4

Math.max.apply(this, [1, 2, 3, 4]) //4

let arr = [1, 2, 3, 4]
function getMax(arr) {
  return arr.reduce((prev, cur, curIndex, arr) => {
    return Math.max(prev, cur);
 }, 0)
}
console.log(getMax(arr))

对象数组 查找id最大的项

const arr = [{ id: 1, name: 'jack' },{ id: 2, name: 'may' },{ id: 3, name: 'shawn' },{ id: 4, name: 'tony' }]
const arr1 = Math.max.apply(Math, arr.map(item => { return item.id }))
const arr2 = arr.sort((a, b) => { return b.id - a.id })[0].id
console.log(arr1) // 4
console.log(arr2) // 4

对象转数组

Object.keys({ name: '张三', age: 14 }) //['name','age']
Object.values({ name: '张三', age: 14 }) //['张三',14]
Object.entries({ name: '张三', age: 14 }) //[[name,'张三'],[age,14]]
Object.fromEntries(['name', '张三'], ['age', 14]) //ES10的api,Chrome不支持 , firebox输出{name:'张三',age:14}

数组转对象

const arrName = ['张三', '李四', '王五']
const arrAge=['20','30','40']
const arrDec = ['描述1', '描述2', '描述3']
function combinationObj(arrName, arrAge, arrDec) {
  return arrName.map((item,index)=>{
    return { name: item, age: arrAge[index],dec:arrDec[index]}
  })
}
console.log(combinationObj(arrName, arrAge, arrDec)) 
// [{ name: '张三', age: '20', dec: '描述1' },{ name: '李四', age: '30', dec: '描述2' },{ name: '王五', age: '40', dec: '描述3' }]

根据关键字查找对象

/*
* list 被查找数组
* key 查找的字段
* value 查找对象的值
* @desc 查找对象
*/
 searchObjByKey(list, key, value) {
    if (!Array.isArray(list)) {
      return [];
    }
    return list.filter(item => {
      return item[key] === value;
    });
  },

字符串

字符串反转

function reverseStr(str = "") {
  return str.split("").reduceRight((t, v) => t + v);
}

const str = "reduce123";
console.log(reverseStr(str)); // "321recuder"

对象

对象是否相等

function deepCompare(x, y) {
  var i, l, leftChain, rightChain;

  function compare2Objects(x, y) {
    var p;

    // remember that NaN === NaN returns false
    // and isNaN(undefined) returns true
    if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') {
      return true;
    }

    // Compare primitives and functions.     
    // Check if both arguments link to the same object.
    // Especially useful on the step where we compare prototypes
    if (x === y) {
      return true;
    }

    // Works in case when functions are created in constructor.
    // Comparing dates is a common scenario. Another built-ins?
    // We can even handle functions passed across iframes
    if ((typeof x === 'function' && typeof y === 'function') ||
      (x instanceof Date && y instanceof Date) ||
      (x instanceof RegExp && y instanceof RegExp) ||
      (x instanceof String && y instanceof String) ||
      (x instanceof Number && y instanceof Number)) {
      return x.toString() === y.toString();
    }

    // At last checking prototypes as good as we can
    if (!(x instanceof Object && y instanceof Object)) {
      return false;
    }

    if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) {
      return false;
    }

    if (x.constructor !== y.constructor) {
      return false;
    }

    if (x.prototype !== y.prototype) {
      return false;
    }

    // Check for infinitive linking loops
    if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) {
      return false;
    }

    // Quick checking of one object being a subset of another.
    // todo: cache the structure of arguments[0] for performance
    for (p in y) {
      if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
        return false;
      } else if (typeof y[p] !== typeof x[p]) {
        return false;
      }
    }

    for (p in x) {
      if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
        return false;
      } else if (typeof y[p] !== typeof x[p]) {
        return false;
      }

      switch (typeof (x[p])) {
        case 'object':
        case 'function':

          leftChain.push(x);
          rightChain.push(y);

          if (!compare2Objects(x[p], y[p])) {
            return false;
          }

          leftChain.pop();
          rightChain.pop();
          break;

        default:
          if (x[p] !== y[p]) {
            return false;
          }
          break;
      }
    }

    return true;
  }

  if (arguments.length < 1) {
    return true; 
  }

  for (i = 1, l = arguments.length; i < l; i++) {

    leftChain = []; //Todo: this can be cached
    rightChain = [];

    if (!compare2Objects(arguments[0], arguments[i])) {
      return false;
    }
  }

  return true;
}

const obj1 = { 
  name: '张三', age: 23, 
  obj: { name: '李四', age: 46 }, 
  arr: [1, 2, 3],
  date:new Date(23),
  reg: new RegExp('abc'),
  fun: ()=>{}
 }
const obj2 = { 
  name: '张三', age: 23, 
  obj: { name: '李四', age: 46 }, 
  arr: [1, 2, 3],
  date: new Date(23),
  reg: new RegExp('abc'),
  fun: ()=>{}
 }

console.log(deepCompare(obj1,obj2)) // true
false 表示重复

判断对象中的某个字段是否重复

function isUniqueByKey(arrObj, key) {
    const obj = {};
    let flag = arrObj.every(item => {
        let res = obj[item[key]] ? false : obj[item[key]] = true;
        return res;
    });
    return flag;
}
let a = [
    { name: '11', code: 12 },
    { name: '114', code: 122 },
    { name: '111', code: 1224 },
]

let isUniqueByName = isUniqueByKey(a, 'name');
let isUniqueByCode = isUniqueByKey(a, 'code');
console.log('是否重复', isUniqueByName, isUniqueByCode);

版本号对比

说明:实现一个方法,用于比较两个版本号(version1、version2)

  • 如果version1 > version2,返回1;如果version1 < version2,返回-1,其他情况返回0
    
  • 版本号规则`x.y.z`,xyz均为大于等于0的整数,至少有x位
    
function compareVersion(version1, version2) {  
   // TODO: 你的代码
  let arr1 = version1.split('.')
  let arr2 = version2.split('.')
  let len = Math.max(arr1.length, arr2.length)
  for (let i = 0; i< len; i++) {
  	if(typeof arr1[i] === 'undefined'){
    	return -1
          
    }else if(typeof arr2[i] === 'undefined'){
    	return 1
    
    }else if(arr1[i] > arr2[i]){
   		return 1
    }else if(arr1[i] < arr2[i]){
    	return -1
    }else{
    	return 0
    }
  }
}
console.log(compareVersion('0.1', '1.1.1'))
console.log(compareVersion('13.37', '1.2 '))
console.log(compareVersion('1.1', '1.1.0'))

url参数

url 参数反序列化

const url = 'http://fligglg,com/index.html?age=25&name=TYJ'

const parseQueryString = url => {
  const urlPar = url.split('?')[1]
  return urlPar.split("&").reduce((t, v) => {
    const [key, val] = v.split("=");
    t[key] = val
    return t;
  }, {});
}

const urlParams = parseQueryString(url)

console.log(urlParams)  // { age: "25", name: "TYJ" }
const search = '?age=25&name=TYJ'

function parseUrlSearch(search) {
  return search.replace(/(^\?)|(&$)/g, "").split("&").reduce((t, v) => {
    const [key, val] = v.split("=");
    t[key] = decodeURIComponent(val);
    return t;
  }, {});
}

console.log(parseUrlSearch(search)); // { age: "25", name: "TYJ" }

url参数序列化

function stringifyUrl(search = {}) {
  return Object.entries(search).reduce(
    (t, v) => `${t}${v[0]}=${encodeURIComponent(v[1])}&`,
    Object.keys(search).length ? "?" : ""
  ).replace(/&$/, "");
}

console.log(stringifyUrl({ age: 27, name: "YZW" })); // "?age=27&name=YZW"

数字千分位

方法一:

function thousandNum(num = 0) {
  const str = (+num).toString().split(".");
  const int = nums => nums.split("").reverse().reduceRight((t, v, i) => t + (i % 3 ? v : `${v},`), "").replace(/^,|,$/g, "");
  const dec = nums => nums.split("").reduce((t, v, i) => t + ((i + 1) % 3 ? v : `${v},`), "").replace(/^,|,$/g, "");
  return str.length > 1 ? `${int(str[0])}.${dec(str[1])}` : int(str[0]);
}

console.log(thousandNum(1234)); // "1,234"
console.log(thousandNum(1234.00)); // "1,234"
console.log(thousandNum(0.1234)); // "0.123,4"
console.log(thousandNum(1234.5678)); // "1,234.567,8"

方法二:

console.log('1234567890'.replace(/\B(?=(\d{3})+(?!\d))/g, ","))
console.log((1234567890).toLocaleString())