工作中常用的10个工具函数

258 阅读6分钟

1.类型判断

核心使用 Object.prototype.toString.call(object) 方法,基本类型和引用类型都适用,比如Array,Date等。

/**
 * @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

2.对象转数组

Array.prototype.slice.call(arguments)能将有length属性的对象转换为数组(特别注意: 这个对象一定要有length属性). 但有一个例外,IE下的节点集合它不能转换(因为IE下的dom对象是以com对象的形式实现,js对象和com对象不能进行转换)

/**
 * @param {objecttarget
 * @param {numberbegin
 * @param {numberend
 * @return {any[]} 
 */
function toArray(target, ...extra) {
  return Array.prototype.slice.call(target, extra);
}

例子:

let obj = {
  length: 2,
  0'first',
  1'second'
};

toArray(obj); // ['first''second']
toArray(obj, 1); // ['second']
toArray(obj, 01); // ['first''second']

3.对象属性删除

/**
 * @param {object} target
 * @param {string[]} props
 * @return {object} 
 */
function omit(target, props = []) {
  if (!props.length) return target;
  
  let result = {};
  
  Object.keys(target).forEach(key => {
    if (!props.includes(key)) {
      result = {
        ...result,
        [key]: target[key]
      };
    }
  });
  
  return result;
}

例子:

let obj = {
  name: 'sky',
  age: 25,
  sex: 'man'
};

omit(obj, ['name']); // { age: 25, sex: 'man' }

4.时间格式化

/**
 * @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 result = 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 result;
}

例子:

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"

5.url参数截取

/**
 * @param {stringtarget
 * @return {string} 
 */
function getUrlParams(target) {
  const str = location.search.substr(1);
  
  if (!str) return '';
  
  const index = str.split('&').findIndex(item => item.includes(`${target}=`));
  
  return str.split('&')[index].split('=')[1];
}

例子:

// http://www.jianshu.com/search?q=123&page=1&type=note

getUrlParams('q'); // "123"

6.性能分析

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
    });
  });
}

7.随机字符串

应用场景 React 列表的 key。

/**
 * @return {string} 
 */
function uuid() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/gfunction(c) {
        var r = Math.random() * 16 | 0,
            v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    }).toUpperCase();
}

例子:

uuid(); // "B0578B1A-1512-42DC-8996-887E9D2C56AA"

8.生成一周时间

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

/**
 * @return {string[]} 
 */
function getWeekTime(){
  return [...new Array(7)].map((j,i)=> new Date(Date.now()+i*8.64e7).toLocaleDateString());
}

例子:

getWeekTime(); // ["2020/8/9""2020/8/10""2020/8/11""2020/8/12""2020/8/13""2020/8/14""2020/8/15"]

9.防抖

触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间

应用场景

  • 搜索框搜索输入。只需用户最后一次输入完,再发送请求。
  • 手机号、邮箱验证输入检测。
  • 窗口大小Resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。
function debounce(fn) {
  let timeout = null// 创建一个标记用来存放定时器的返回值
  return function () {
    if (timeout !== null)clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
    timeout = setTimeout(() => { // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
      fn.apply(thisarguments);
    }, 500);
  };
}

例子:

function sayHi() {
  console.log('防抖成功');
}

const inp = document.getElementById('inp');
inp.addEventListener('input'debounce(sayHi));

10.节流

高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率

应用场景

  • 滚动加载,加载更多或滚到底部监听。
  • 谷歌搜索框,搜索联想功能。
  • 高频点击提交,表单重复提交。
function throttle(fn) {
  let canRun = true// 通过闭包保存一个标记
  return function () {
    if (!canRun) return// 在函数开头判断标记是否为true,不为true则return
    canRun = false// 立即设置为false
    setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
      fn.apply(thisarguments);
      // 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉
      canRun = true;
    }, 500);
  };
}

例子:

function sayHi(e) {
  console.log(e.target.innerWidth, e.target.innerHeight);
}

window.addEventListener('resize'throttle(sayHi));