Skip to content

轻函数

封装常用量、轻量的工具函数。

1️⃣ 性能优化

防抖

debounce

防抖函数:指定时间间隔内相同的事件触发只执行最后一次。

javascript
/**
 * 前端性能优化:防抖函数
 * @param {Function} fn 防抖处理回调
 * @param {Number} delay 时间间隔阈值
 * @returns {Function} 封装好的防抖函数
 */
export const debounce = (fn, delay = 200) => {
  let timer;
  return function () {
    const context = this;
    const args = arguments;
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(context, args);
    }, delay);
  };
};

节流

throttle

节流函数: 指定时间间隔内只会执行一次任务。

javascript
/**
 * 前端性能优化:节流函数
 * @param {Function} fn 节流处理回调
 * @param {Number} delay 时间间隔阈值
 * @returns {Function} 封装好的节流函数
 */
export const throttle = (fn, delay = 200) => {
  let timer;
  return function () {
    if (!timer) {
      fn.apply(this, arguments);
      timer = setTimeout(() => {
        timer = null;
      }, delay);
    }
  };
},

2️⃣ 时间处理

时间戳

GetTimestamp

获取现在的时间戳

javascript
/**
 * 获取当前时刻的时间戳
 * @return {String} 时间戳
 */
export const getTimestamp = () => {
  return new Date().getTime();
};

时间戳格式

TimestampToFormatTime

将时间戳转换为用户自定义的时间格式

javascript
/**
 * 将时间戳转换为用户自定义的时间格式
 * @param {Number} timestamp 时间戳
 * @param {String} rule 时间格式
 * @returns {String}
 */
export const timestampToFormatTime = (timestamp, rule = "yyyy-MM-dd HH:mm:ss") => {
  const D = new Date(timestamp);
  const timeObj = {};
  const rules = rule.match(/\w+/g);
  let ft = rule;

  timeObj["yyyy"] = D.getFullYear();
  timeObj["MM"] = D.getMonth() + 1;
  timeObj["dd"] = D.getDate();
  timeObj["HH"] = D.getHours();
  timeObj["mm"] = D.getMinutes();
  timeObj["ss"] = D.getSeconds();
  timeObj["ms"] = D.getMilliseconds();

  rules.map((f) => {
    let ff = f.length === 1 ? `${f}${f}` : f;
    ft = ft.replace(new RegExp(f, "g"), fillingZero(f, timeObj[ff]));
  });

  return ft;
};

时间格式

FormatTime

根据规则,对时间进行格式处理

javascript
/**
 * 根据规则,对时间进行格式处理
 * @param {String} time 需格式的时间 2023-05-23T12:25:42.9703802 || 2023-05-23 12:25:42.9703802
 * @param {String} rule 格式规则 yyyy-MM-dd HH:mm:ss || yyyy-M-d H:m:s 对填充零进行了判断
 * @returns {String}
 */
export const formatTime = (time, rule = "yyyy-MM-dd HH:mm:ss") => {
  const times = time.match(/\d+/g);
  const timeObj = {};
  const rules = rule.match(/\w+/g);
  let mapping = ["yyyy", "MM", "dd", "HH", "mm", "ss", "ms"];
  let ft = rule;

  // 数组转对象
  times.map((t, k) => {
    timeObj[mapping[k]] = times[k];
  });

  rules.map((f) => {
    let ff = f.length === 1 ? `${f}${f}` : f;
    ft = ft.replace(new RegExp(f, "g"), timeObj[ff]);
    // ft = ft.replace(new RegExp(f, "g"), fillingZero(f, timeObj[ff])); // 可搭配 fillingZero 使用
  });

  return ft;
};

前补零

FillingZero

根据时间字段名,自动判断是否需要填充零

javascript
/**
 * 根据时间字段名,自动判断是否需要填充零
 * @param {String} field 时间字段名
 * @param {String} value 预处理值
 * @returns {String|Number}
 */
export const fillingZero = (field, value) => {
  switch (field) {
    case "MM":
    case "dd":
    case "HH":
    case "mm":
    case "ss":
      return value < 10 ? `0${+value}` : value;
    case "M":
    case "d":
    case "H":
    case "m":
    case "s":
      return +value;
    default:
      return value;
  }
};

3️⃣ 数学运算

四则运算

Calc

基本数学运算,能够有效避免 javascript 精度问题。

javascript
/**
 * 基本数学运算,能够有效避免javascript精度问题。
 * @param { String | Number } num1 操作数
 * @param { String | Number } num2 被操作数
 * @param { String } type 计算类型
 * @return { Number } result 计算结果
 */
function calc(num1, num2, type) {
  const n1 = num1.toString().split(".")[1] ?? "0";
  const n2 = num2.toString().split(".")[1] ?? "0";
  const point = 10 ** (+n1 > +n2 ? n1.length : n2.length); // 精度
  let result = 0;
  switch (type) {
    case "+":
      result = (num1 * point + num2 * point) / point;
      break;
    case "-":
      result = (num1 * point - num2 * point) / point;
      break;
    case "*":
      result = (num1 * point * (num2 * point)) / point / point;
      break;
    case "/":
      result = (num1 * point) / (num2 * point);
      break;
  }
  return result;
}

随机数

GetRandomInt

获取指定间距 [min, max] 内的随机整数

javascript
/**
 * 获取指定间距 [min, max] 内的随机整数
 * @param {Number} min 最小值
 * @param {Number} max 最大值
 * @returns 随机整数
 */
export const getRandomInt = (min, max) => {
  // 使用 Math.floor() 向下取整,确保结果是整数
  // 使用 Math.random() 生成一个介于 0 到 1 之间的随机小数
  // 然后将其乘以 (max - min + 1) 来获取一个介于 0 到 (max - min) 之间的随机小数
  // 最后再加上 min,将结果移动到指定的间距内
  return Math.floor(Math.random() * (max - min + 1)) + min;
};

随机数(相邻去重)

GetRandomIntWithExclusion

随机一个指定区间的整型数值,且允许限制重值,避免连续随机数重复

javascript
/**
 * 随机一个指定区间的整型数值,且允许限制重值,避免连续随机数重复
 * @param {Number} min 最小值
 * @param {Number} max 最大值
 * @param {Number|undefined} excludeValue 限制重值
 * @returns {Number}
 */
export const getRandomIntWithExclusion = (min, max, excludeValue) => {
  let randomValue;
  do {
    randomValue = Math.floor(Math.random() * (max - min + 1)) + min;

    // 结合 getRandomInt 的使用
    // randomValue =  getRandomInt(min, max)
  } while (excludeValue !== undefined && randomValue === excludeValue);
  return randomValue;
};

4️⃣ 面试手撕函数

替换字符串

ReplaceString

替换指定字符串

javascript
/**
 * 替换指定字符串
 * @param {String} sourceStr 源修改内容
 * @param {String} checkStr 需检验匹配的内容
 * @param {String} replaceStr 计划替换内容
 * @returns {String} 替换结果
 */
function replaceString(sourceStr, checkStr, replaceStr = "") {
  if (!sourceStr) return console.error("sourceStr 为必传字段");
  if (!checkStr) return console.error("checkStr 为必传字段");
  const reg = new RegExp(checkStr);
  return sourceStr.replace(reg, replaceStr);
},

千分位分隔符

千分位分隔符

javascript
function thousandSeparator(number) {
  let result = [];
  let rest = String(number);
  while (rest.length) {
    result.unshift(rest.slice(-3));
    rest = rest.slice(0, -3);
  }
  const tempRes = result.join(",");
  return tempRes
}
console.log('thousandSeparator:', thousandSeparator(12378900)) // 12,378,900

检查是否是类的对象实例

检查是否是类的对象实例

javascript
const checkIfInstanceOf = (obj, classFunction) => {
  if (classFunction === null) return false;
  while (obj !== null) {
    if (obj.__proto__ === classFunction.prototype) {
      return true
    };
    obj = obj.__proto__;
  }
  return false;
}
class Animal {};
class Dog extends Animal {};

console.log('checkIfInstanceOf', checkIfInstanceOf(new Date(), Date)) // true
console.log('checkIfInstanceOf', checkIfInstanceOf(new Dog(), Animal)) // true
console.log('checkIfInstanceOf', checkIfInstanceOf(Date, Date)) // false
console.log('checkIfInstanceOf', checkIfInstanceOf(5, Number)) // true
console.log('checkIfInstanceOf', checkIfInstanceOf([], Array)) // true

浅拷贝

ShallowClone

浅拷贝

javascript
/**
 * 浅拷贝对象或数组
 * @param {any} value - 需要浅拷贝的值
 * @returns {any} - 浅拷贝后的值
 */
function shallowClone(value) {
  if (value === null || typeof value !== "object") {
    return value;
  }

  const type = Object.prototype.toString.call(value);

  if (type === "[object Array]") {
    return [...value];
  } else if (type === "[object Object]") {
    return { ...value };
  } else {
    return value; // 对于 Date, RegExp, Map, Set 等非普通对象类型,直接返回原值
  }
}

深拷贝

DeepClone

深拷贝

javascript
/**
 * 深拷贝对象或数组
 * @param {any} value - 需要深拷贝的值
 * @returns {any} - 深拷贝后的值
 */
fconst deepClone = obj => {
  if (obj === null) return null;
  let clone = Object.assign({}, obj);
  Object.keys(clone).forEach(
    key =>
      (clone[key] =
        typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key])
  );
  if (Array.isArray(obj)) {
    clone.length = obj.length;
    return Array.from(clone);
  }
  return clone;
};

const a = { foo: 'bar', obj: { a: 1, b: 2 } };
const b = deepClone(a); // a !== b, a.obj !== b.obj

数组的交集

intersect

js
// 在西雅图的公司远程工作,因为月中算法考核,半月的绩效奖金没了。。。
const intersect = function (nums1, nums2) {
  let result = []
  let longArr = nums1.length > nums2.length ? nums1 : nums2;
  let shortArr = nums1.length > nums2.length ? nums2 : nums1;

  for (let i = 0; i < shortArr.length; i++) {
    let longIndex = longArr.indexOf(shortArr[i])
    if (longIndex != -1) {
      result.push(longArr.splice(longIndex, 1)[0])
    }
  }
  return result
};

intersect([1,2,1,2], [2,2,2]) // [2, 2, 2]
intersect([4,9,5], [9,4,9,8,4]) // [4, 9]
intersect([3,1,2], [2,2]) // [2]

数组排序

sortArray

js
/**
 * @param {number[]} nums
 * @return {number[]}
 */
const sortArray = function (nums) {
  const { length } = nums;
  for (let i = 0; i < length; i++) {
    for (let j = 0; j < length - 1 - i; j++) {
      if (nums[j] > nums[j + 1]) {
        [nums[j], nums[j + 1]] = [nums[j + 1], nums[j]];
      }
    }
  }
  return nums;
};
sortArray([1, 3, 9, 5, 2, 4, 6])

柯里化

柯里化

js
/* 
在案例中,curry函数接受一个函数fn作为参数,返回一个新的函数curried。
curried函数在调用时会检查传入的参数数量是否大于或等于原始函数fn的参数数量(arity),
如果是,则直接调用原始函数;否则,返回一个新的函数,该函数接受剩余的参数(rest),
并将之前传入的参数(args)与剩余参数合并后调用curried函数。通过这种方式,实现函数柯里化。
*/

const curry = (fn) => {
  const arity = fn.length;
  return function curried(...args) {
    if (args.length >= arity) {
      return fn.apply(this, args);
    } else {
      return function (...rest) {
        return curried.apply(this, args.concat(rest));
      };
    }
  };
}
const getURL = (protocol, domain, path) => {
  return protocol + "://" + domain + "/" + path;
}
const myurl = getURL('http', 'mysite', 'home.html');
const myurl2 = getURL('http', 'mysite', 'about.html');
console.log('myurl', myurl);
console.log('myurl2', myurl2);

// 减少重复传递不变的部分参数
const superGetURL = curry(getURL)('https', 'mysite');
const myurl3 = superGetURL('detail.html')

console.log('myurl3', myurl3);

Released under the MIT License.