🔥0202年了,几个基础的手写函数总得会吧

1,962 阅读3分钟

这几天看到一个大三大佬面试字节跳动的蚊子,突然觉得自己太辣鸡了···校招的题我一半多都不会啊···赶紧潜下心来学习学习提(an)高(wei)自己,边翻掘金边谷歌,简单实现了几个常用函数···(借鉴了太多人的文章了··弄不清了··没法写出引用了··大佬们请谅解··)

深拷贝 deepClone

function deepClone(target, hash = new WeakMap()) {
  // 不是对象或者是null 直接返回
  if (typeof target !== "object" || target == null) return target;
  // 通过WeakMap判断是否已有 有则返回对象引用
  if (hash.has(target)) return hash.get(target);
  // 判断是数组还是字符串
  const result = target instanceof Array ? [] : {};
  // 结果放进WeakMap里,key是target,值是result
  hash.set(target, result);
  // for in遍历
  for (const key in target) {
    // 避开原型链上的属性
    if (Object.prototype.hasOwnProperty.apply(target,key)) {
      // 递归调用
      result[key] = _deepClone(target[key]);
    }
  }
  // 返回结果
  return result;
}

防抖 debounce

function debounce(handler, wait = 300, immediate = false) {
  // 声明一个变量timer
  let timer;
  // 判断参数是否符合预期
  if (typeof handler !== "function") throw new TypeError("The first params shoule be a Function");
  if (typeof wait !== "number") throw new TypeError("The second params shoule be a Number");

  return function() {
    // 缓存上下文和参数
    const context = this,
      args = [...arguments];
    // 如果timer存在就清除timer
    timer && clearTimeout(timer);

    if (immediate) {
      // immediate为true且定时器不存在,则设置定时器,并在指定时间后timer设置为null
      const callNow = !timer;
      timer = setTimeout(() => {
        timer = null;
      }, wait);
      // 且立即执行一次handler,通过apply绑定上下文和传入参数
      callNow && handler.apply(context, args);
    } else {
      // immediate为false则设置定时器
      timer = setTimeout(() => {
        handler.apply(context, args);
      }, wait);
    }
  };
}

节流 throttle

function throttle(handler, wait = 300) {
  // 声明一个变量timer
  let timer;
  return function() {
    // timer存在则直接返回怎么也不干
    if (timer) return;
    // 缓存上下文和参数
    const context = this,
      args = [...arguments];
    // 设置定时器
    timer = setTimeout(() => {
      // 执行handler,通过apply绑定上下文和传入参数
      handler.apply(context, args);
      // timer设置为null
      timer = null;
    }, wait);
  };
}

是否是纯对象 isPlainObject

function isPlainObject(obj) {
  return Object.prototype.toString.call(obj) === "[object Object]";
}

深度对比 isEqual

function isEqual(target1, target2) {
  // 直接先对比,相等就返回
  if (target1 === target2) return true;

  const isPlainObject = obj => Object.prototype.toString.call(obj) === "[object Object]";
  //  如果两者任意一个不是纯对象,直接返回对比结果
  if (!isPlainObject(target1) || !isPlainObject(target2)) return target1 === target2;

  // 拿到两个keys的数组,长度不一致那肯定不一样
  const target1Keys = Object.keys(target1);
  const target2Keys = Object.keys(target2);
  if (target1Keys.length !== target2Keys.length) return false;

  // 以target1为基准遍历,递归调用isEqual对比每一项,任何一项不一样直接返回false
  for (const key in target1) {
    if (target1.hasOwnProperty(key)) {
      if (!isEqual(target1[key], target2[key])) return false;
    }
  }
  // 全一样
  return true;
}

拍平数组 flatArray

function flatArray(arr, depth = Infinity) {
  // 不是数组则报错
  if (!arr instanceof Array) throw new TypeError("Expect Array");
  // 判断是否支持ES10的flat方法
  if (Array.prototype.flat) {
    // 支持则直接调用
    return arr.flat(depth);
  } else {
    // 看数组内元素是否都是普通类型
    const isDeep = arr.some(item => Array.isArray(item));
    // 如果都是普通类型则直接返回
    if (!isDeep) return arr;
    // 用数组concat方法拍平数组第一层
    const result = [].concat(...arr);
    // 递归调用,拍平深层数组
    return flatArray(result);
  }
}

数组快排 quickSort

  function quickSort(target) {
    // 不是数组或者数组长度小于2,则直接返回
    if (Object.prototype.toString.apply(target) !== '[object Array]' || target.length < 2) return target
    // 拷贝一份数组
    const _arr = [...target]
    // 找到并取出基准元素
    const pivotItem = _arr.splice(Math.floor(_arr.length / 2), 1)
    // 定义两个临时数组,分别储存比基准小和比基准大的元素
    const smallerArr = []
    const biggerArr = []
    // 遍历数组,将元素分类
    for (let index = 0; index < _arr.length; index++) {
      const element = _arr[index]
      pivotItem > element ? smallerArr.push(element) : biggerArr.push(element)
    }
    // 将两个临时数组递归调用,并将两个数组和基准元素合并起来
    const result = this._quickSort(smallerArr).concat(pivotItem, this._quickSort(biggerArr))
    // 返回新数组
    return result
  },

数组冒泡排序 bubbleSort

  function bubbleSort(target) {
    // 不是数组或者数组长度小于2,则直接返回
    if (Object.prototype.toString.apply(target) !== '[object Array]' || target.length < 2) return target
    // 拷贝一份数组
    const _arr = [...target]
    // 遍历数组
    for (let index = 0; index < _arr.length; index++) {
      for (let innerIdx = index + 1; innerIdx < _arr.length; innerIdx++) {
        // 相邻元素两两对比,元素交换,大的元素交换到后面
        if (_arr[index] > _arr[innerIdx]) {
          [_arr[index], _arr[innerIdx]] = [_arr[innerIdx], _arr[index]]
        }
      }
    }
    // 返回新数组
    return _arr
  }