JS工具函数

知识库编程语言Web基础JavaScript大约 7 分钟

slice,splice,split的区别

  • slice 用法:array.slice(start,end),含头不含尾 解释:该方法是对数组或者字符串进行部分截取,并返回一个数副本;

    console.log([1,2,3,4,5,6].slice(0,3));    //[1,2,3]
    console.log([1,2,3,4,5,6].slice(3));      //[4,5,6]
    console.log([1,2,3,4,5,6].slice(8));      //[]
    console.log([1,2,3,4,5,6].slice(-1));     //[6]
    console.log([1,2,3,4,5,6].slice(-8));     //[1, 2, 3, 4, 5, 6]
    console.log([1,2,3,4,5,6].slice(2,-3));   //[3]
    console.log("123456".slice(0,3));       //"123"
    console.log("123456".slice(3));         //"456"
    console.log("123456".slice(8));         //""
    console.log("123456".slice(-1));        //"6"
    console.log("123456".slice(-8));        //"123456"
    console.log("123456".slice(2,-3));      //"3"
    
  • splice 用法:array.splice(start,deleteCount,item...) 解释:splice方法从array中移除一个或多个数组,并用新的item替换它们。 参数:

    • start是从数组array中移除元素的开始位置。
    • deleteCount是要移除的元素的个数。
    • 如果有额外的参数,那么item会插入到被移除元素的位置上。 返回:一个包含被移除元素的数组。
    var a=['a','b','c'];
    console.log(a.splice(1,1,'e','f'));  //["b"]
    console.log(a);       //["a", "e", "f", "c"]
    
  • split 用法:string.split(separator,limit) 解释:split方法把这个string分割成片段来创建一个字符串数组。 参数:

    • separator参数可以是一个字符串或一个正则表达式
    • limit可以限制被分割的片段数量
    console.log("0123456".split("",3));  //b=["0","1","2"]
    

生成一周时间

new Array 创建的数组只是添加了length属性,并没有实际的内容。通过扩展后,变为可用数组用于循环。

function getWeekTime(){
  return [...new Array(7)].map((j,i)=> new Date(Date.now()+i*8.64e7).toLocaleDateString())
}
getWeekTime();// ["2020/2/26", "2020/2/27", "2020/2/28", "2020/2/29", "2020/3/1", "2020/3/2", "2020/3/3"]

获取元素类型

const dataType = obj => Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/, '$1').toLowerCase();
dataType("");   //"string"

类 型 判 断

判断核心使用Object.prototype.toString,这种方式可以准确的判断数据类型。

 /**
 * @param {any} target
 * @param {string} type
 * @return {boolean}
 */
function isType(target, type) {
    let targetType = Object.prototype.toString.call(target).slice(8, -1).toLowerCase()
    return targetType === type.toLowerCase()
}
isType([], 'Array');                // true
isType(/\d/, 'RegExp');             // true
isType(new Date(), 'Date');         // true
isType(function(){}, 'Function');   // true
isType(Symbol(1), 'Symbol');        // true

简单的交换变量

你可能使用第三个变量 temp 交换两个变量。但是这个技巧将向你展示一种使用解构来交换变量的新方法。

var a = 6;
var b = 7;
[a,b] = [b,a]
console.log(a,b) // 7 6

对象属性剔除

应用场景很简单,当你需要使用一个对象,但想移除部分属性时,可以使用该方法。同样的,你可以实现一个对象属性选取方法。

/**
 * @param {object} object
 * @param {string[]} props
 * @return {object}
 */
function omit(object, props=[]){
    let res = {};
    Object.keys(object).forEach(key=>{
        if (props.includes(key) === false) {
            res[key] = typeof object[key] === 'object' && object[key] !== null ?
            jsON.parse(jsON.stringify(object[key])):
            object[key]
        }
    });
    return res
}
omit({id: 1,title: 'title',comment: []}, ['id']);       // {title: 'title', comment: []}

日期格式化

一个很灵活的日期格式化函数,可以根据使用者给定的格式进行格式化,能应对大部分场景。

/**
 * @param {string} format
 * @param {number} timestamp - 时间戳
 * @return {string}
 */
function formatDate(format='Y-M-D h:m', timestamp=Date.now()){
    let date = new Date(timestamp)
    let dateInfo = {
    Y: date.getFullYear(),
    M: date.getMonth()+1,
    D: date.getDate(),
    h: date.getHours(),
    m: date.getMinutes(),
    s: date.getSeconds()
    }
    let formatNumber = (n) => n > 10 ? n : '0' + n
    let res = format
    .replace('Y', dateInfo.Y)
    .replace('M', dateInfo.M)
    .replace('D', dateInfo.D)
    .replace('h', formatNumber(dateInfo.h))
    .replace('m', formatNumber(dateInfo.m))
    .replace('s', formatNumber(dateInfo.s))
    return res
}
formatDate();                   // "2020-2-24 13:44"
formatDate('M月D日 h:m');        // "2月24日 13:45"
formatDate('h:m Y-M-D', 1582526221604);     //"14:37 2020-2-24"

性 能 分 析

Web Performance API允许网页访问某些函数来测量网页和Web应用程序的性能。 performance.timing 包含延迟相关的性能信息。 performance.memory 包含内存信息,是Chrome中添加的一个非标准扩展,在使用时需要注意。

window.onload = function(){
  setTimeout(()=>{
    let t = performance.timing,
        m = performance.memory
    console.table({
      'DNS查询耗时': (t.domainLookupEnd - t.domainLookupStart).toFixed(0),
      'TCP链接耗时': (t.connectEnd - t.connectStart).toFixed(0),
      'request请求耗时': (t.responseEnd - t.responseStart).toFixed(0),
      '解析dom树耗时': (t.domComplete - t.domInteractive).toFixed(0),
      '白屏时间': (t.responseStart - t.navigationStart).toFixed(0),
      'domready时间': (t.domContentLoadedEventEnd - t.navigationStart).toFixed(0),
      'onload时间': (t.loadEventEnd - t.navigationStart).toFixed(0),
      'js内存使用占比': m ? (m.usedjsHeapSize / m.totaljsHeapSize * 100).toFixed(2) + '%' : undefined
    })
  })
}

延迟函数delay

const delay = ms => new Promise((resolve, reject) => setTimeout(resolve, ms));
const getData = status => new Promise((resolve, reject) => {
     status ? resolve('done') : reject('fail')
});
const getRes = async (data) => {
     try {
         const res = await getData(data)
         const timestamp = new Date().getTime()
         await delay(1000);
         console.log(res, new Date().getTime() - timestamp)
     } catch (error) {
         console.log(error)
     }
 };
getRes(true); // 隔了1秒

防 抖

性能优化方案,防抖用于减少函数请求次数,对于频繁的请求,只执行这些请求的最后一次。

基础版本

function debounce(func, wait = 300){
  let timer = null;
  return function(){
    if(timer !== null){
      clearTimeout(timer);
    }
    timer = setTimeout(fn,wait);
  }
}

改进版本添加是否立即执行的参数,因为有些场景下,我们希望函数能立即执行。

/**
 * @param {function} func - 执行函数
 * @param {number} wait - 等待时间
 * @param {boolean} immediate - 是否立即执行
 * @return {function}
 */
function debounce(func, wait = 300, immediate = false){
    let timer, ctx;
    let later = (arg) => setTimeout(()=>{
    func.apply(ctx, arg)
    timer = ctx = null
    }, wait)
    return function(...arg){
    if(!timer){
      timer = later(arg)
      ctx = this
      if(immediate){
        func.apply(ctx, arg)
      }
    }else{
      clearTimeout(timer)
      timer = later(arg)
    }
    }
}
let scrollHandler = debounce(function(e){
  console.log(e)
}, 500);

节 流

性能优化方案,节流用于减少函数请求次数,与防抖不同,节流是在一段时间执行一次。

/**
 * @param {function} func - 执行函数
 * @param {number} delay - 延迟时间
 * @return {function}
 */
function throttle(func, delay){
    let timer = null
    return function(...arg){
    if(!timer){
      timer = setTimeout(()=>{
        func.apply(this, arg)
        timer = null
      }, delay)
    }
    }
}
let scrollHandler = throttle(function(e){
  console.log(e)
}, 500);

base64数据导出文件下载

/**
 * @param {string} filename - 下载时的文件名
 * @param {string} data - base64字符串
 */
function downloadFile(filename, data){
    let downloadLink = document.createElement('a');
    if ( downloadLink ){
    document.body.appendChild(downloadLink);
    downloadLink.style = 'display: none';
    downloadLink.download = filename;
    downloadLink.href = data;
    if ( document.createEvent ){
      let downloadEvt = document.createEvent('MouseEvents');
      downloadEvt.initEvent('click', true, false);
      downloadLink.dispatchEvent(downloadEvt);
    } else if ( document.createEventObject ) {
      downloadLink.fireEvent('onclick');
    } else if (typeof downloadLink.onclick == 'function' ) {
      downloadLink.onclick();
    }
    document.body.removeChild(downloadLink);
    }
}

检测是否为PC端浏览器

function isPCBroswer() {
  let e = window.navigator.userAgent.toLowerCase()
    , t = "ipad" == e.match(/ipad/i)
    , i = "iphone" == e.match(/iphone/i)
    , r = "midp" == e.match(/midp/i)
    , n = "rv:1.2.3.4" == e.match(/rv:1.2.3.4/i)
    , a = "ucweb" == e.match(/ucweb/i)
    , o = "android" == e.match(/android/i)
    , s = "windows ce" == e.match(/windows ce/i)
    , l = "windows mobile" == e.match(/windows mobile/i);
  return !(t || i || r || n || a || o || s || l)
}

识别浏览器及平台

function getPlatformInfo(){
  //运行环境是浏览器
  let inBrowser = typeof window !== 'undefined';
  //运行环境是微信
  let inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform;
  let weexPlatform = inWeex && WXEnvironment.platform.toLowerCase();
  //浏览器 UA 判断
  let UA = inBrowser && window.navigator.userAgent.toLowerCase();
  if(UA){
    let platforms = {
      IE: /msie|trident/.test(UA),
      IE9: UA.indexOf('msie 9.0') > 0,
      Edge: UA.indexOf('edge/') > 0,
      Android: UA.indexOf('android') > 0 || (weexPlatform === 'android'),
      IOS: /iphone|ipad|ipod|ios/.test(UA) || (weexPlatform === 'ios'),
      Chrome: /chrome\/\d+/.test(UA) && !(UA.indexOf('edge/') > 0),
    }
    for (const key in platforms) {
      if (platforms.hasOwnProperty(key)) {
        if(platforms[key]) return key
      }
    }
  }
}

函数柯里化

const curring = fn => {
     const { length } = fn
     const curried = (...args) => {
         return (args.length >= length
               ? fn(...args)
               : (...args2) => curried(...args.concat(args2)))
     }
     return curried
 };
 const listMerge = (a, b, c) => [a, b, c]
 const curried = curring(listMerge);

 console.log(curried(1)(2)(3));     // [1, 2, 3]
 console.log(curried(1, 2)(3));     // [1, 2, 3]
 console.log(curried(1, 2, 3));     // [1, 2, 3]

字符串前面空格去除与替换

const trimStart = str => str.replace(new RegExp('^([\\s]*)(.*)$'), '$2');

console.log(trimStart(' abc '));        // abc
console.log(trimStart('123 '));          // 123

字符串后面空格去除与替换

const trimEnd = str => str.replace(new RegExp('^(.*?)([\\s]*)$'), '$1')

console.log(trimEnd(' abc '));      //   abc
console.log(trimEnd('123 '));       // 123

获取当前子元素是其父元素下子元素的排位

const getIndex = el => {
     if (!el) {
         return -1
     }
     let index = 0;
     do {
         index++
     } while (el = el.previousElementSibling);
     return index
 }

获取当前元素相对于document的偏移量

const getOffset = el => {
    const {top,left} = el.getBoundingClientRect();
    const {scrollTop,scrollLeft} = document.body;
    return {top: top + scrollTop,left: left + scrollLeft}
}

判断是否是移动端

const isMobile = () => 'ontouchstart' in window;

fade动画

const fade = function(el, type = 'in') {
     el.style.opacity = (type === 'in' ? 0 : 1)
     let last = +new Date();
     const tick = () => {
         const opacityValue = (type === 'in' 
                             ? (new Date() - last) / 400
                             : -(new Date() - last) / 400)
         el.style.opacity = +el.style.opacity + opacityValue
         last = +new Date();
         if (type === 'in'
           ? (+el.style.opacity < 1)
           : (+el.style.opacity > 0)) {
             requestAnimationFrame(tick)
         }
     };
     tick();
 };

将指定格式的字符串解析为日期字符串

const dataPattern = (str, format = '-') => {
     if (!str) {
         return new Date()
     }
     const dateReg = new RegExp(`^(\\d{2})${format}(\\d{2})${format}(\\d{4})$`)
     const [, month, day, year] = dateReg.exec(str);
     return new Date(`${month}, ${day} ${year}`)
 };

console.log(dataPattern('12-25-1995'));         // Mon Dec 25 1995 00:00:00 GMT+0800 (中国标准时间)

禁止网页复制粘贴

const html = document.querySelector('html')
 html.oncopy = () => false;
 html.onpaste = () => false;

input框限制只能输入中文

const input = document.querySelector('input[type="text"]')
const clearText = target => {
     const {value} = target;
     target.value = value.replace(/[^\u4e00-\u9fa5]/g, '')
 };
 input.onfocus = ({target}) => {
     clearText(target)
 };
 input.onkeyup = ({target}) => {
     clearText(target)
 };
 input.onblur = ({target}) => {
     clearText(target)
 };
 input.oninput = ({target}) => {
     clearText(target)
 }

去除字符串中的html代码

const removehtml = (str = '') => str.replace(/<[\/\!]*[^<>]*>/ig, '');
console.log(removehtml('<h1>哈哈哈哈<呵呵呵</h1>'));       // 哈哈哈哈<呵呵呵