手撕代码

发布于 2024-06-16  31 次阅读


手写Call

Function.prototype.myCall = function (thisArg, ...args) {
  const fn = this;
  thisArg =
    thisArg !== null && thisArg !== undefined ? Object(thisArg) : window;
  thisArg.fn = fn;
  const result = thisArg.fn(...args);
  delete thisArg.fn;
  return result;
};

手写Apply

Function.prototype.myApply = function (thisArg, args) {
  const fn = this;
  thisArg =
    thisArg === null || thisArg === undefined ? Object(thisArg) :window ;
  thisArg.fn = fn;
//需要对第二个参数进行一下判断,因为需要传入的是一个数组
  args = args || [];
  const result = thisArg.fn(...args);
  delete thisArg.fn;
  return result;
};

手写Bind

Function.prototype.myBind = function (thisArg, ...args) {
  const fn = this;
  thisArg =
    thisArg === null || thisArg === undefined ? window : Object(thisArg);
//bind 调用的时候不会立即执行,而是返回一个函数
  return function (...newArgs) {
    thisArg.fn = fn;
    const result = thisArg.fn(...args, ...newArgs);
    delete thisArg.fn;
    return result;
  };
};

手写柯里化

function curry(func) {

  return function curried(...args) {
    if (args.length >= func.length) {
      return func.apply(this, args);
    } else {
      return function(...args2) {
        return curried.apply(this, args.concat(args2));
      }
    }
  };

}

防抖函数

function debounce(func,delay){
    let timer = null
    const _debounce = function (...args){
        if(timer) clearTimeout(timer)
        timer = setTimeout(() => {
            func.apply(this,args)
        },delay)
    }
    return _debounce
}

立即执行一次防抖

在上一个常规写法的基础上,我们增加了一个传参:immediate,表示是否开启立即执行;

然后和 timer 变量一样,利用闭包的技术,定义一个isInvoke,如果为 false 就是还没有立即执行,就去执行一次,并将isInvoke值改掉

function debounce2(func,delay,immediate = false){
    let timer = null
    let isInvoke = false
    const _debounce = function (...args){
        if(timer) clearTimeout(timer)
        if(immediate && !isInvoke){
            func.apply(this,args)
            isInvoke = true
        }else {
            timer = setTimeout(() => {
                func.apply(this,args)
                isInvoke = false
            },delay)
        }
    }
    return _debounce
}

带取消功能

我们在立即执行一次的基础上,再进行封装,让他支持回调操作,以及取消定时器。

取消定时器很简单,就是给返回的那边变量添加一个属性cancel,这个 cancel 用来清除定时器,以及通过闭包技术将 timer 和 isInvoke 设置为初始值;

支持回调则只需要在函数执行完毕后,判断函数是否有返回值,如果有,则传入回调函数中执行。

function debounce3(fn, delay, immediate = false, resultCallback) {
  let timer = null;
  let isInvoke = false;
  const _debounce = function (...args) {
    if (timer) clearTimeout(timer);
    // 判断是否需要立即执行
    if (immediate && !isInvoke) {
      const result = fn.apply(this, args);
      if (resultCallback) resultCallback(result);
      isInvoke = true;
    } else {
      // 延迟执行
      timer = setTimeout(() => {
        const result = fn.apply(this, args);
        if (resultCallback) resultCallback(result);
        isInvoke = false;
      }, delay);
    }
  };
  // 封装取消功能
  _debounce.cancel = function () {
    if (timer) clearTimeout(timer);
    timer = null;
    isInvoke = false;
  };
  return _debounce;
}

节流函数

function throttle(func,interval){
    let lastTime = 0
    const _throttle = function (...args){
        const nowTime = new Date().getTime()
        const remainTime = interval - (nowTime - lastTime)
        if(remainTime <= 0){
            func.apply(this,args)
            lastTime = nowTime
        }
    }
    return _throttle
}

深拷贝

JSON实现

这是通过将对象转换成字符串,然后再转换成对象实现的深拷贝,但是会存在一定的问题,比如:函数

const cloneObj = JSON.parse(JSON.stringify(obj))

递归方式实现

  1. 首先需要判断传入的对象是否是一个对象类型,如果不是对象类型直接返回即可。
  2. 如果是对象类型,那么就需要去递归调用 deepCopy 函数。
  3. 如果他的 key 是一个 Symbol, 就需要 Object.getOwnPropertySymbols 获取到 symbol 的 key,然后去递归调用。
  4. 然后对传入的数据进行一系列的判断,进行对应的处理。
  5. 如果是循环引用,就需要用到他的第二个参数 map,初始化是一个 WeakMap 数据,我们每次遍历的时候都会在 map 中将当前这个对象作为 WeakMap 的 key 值存起来,如果发现有一样的存在,那就说明存在递归调用,直接 return 对应的值。
function isObject(value) {
  const valueType = typeof value;
  return value !== null && (valueType === "object" || valueType === "function");
}

function deepCopy(originValue, map = new WeakMap()) {
  // 判断是否是一个Set类型,此处是浅拷贝
  if (originValue instanceof Set) {
    return new Set([...originValue]);
  }

  // 判断是否是一个Map类型
  if (originValue instanceof Map) {
    return new Map([...originValue]);
  }

  // 如果是Symbol类型
  if (typeof originValue === "symbol") {
    return Symbol(originValue.description);
  }

  // 如果是函数类型,那么直接使用同一个函数
  if (typeof originValue === "function") {
    return originValue;
  }

  // 判断传入的OriginValue是否是一个对象类型
  if (!isObject(originValue)) {
    return originValue;
  }

  // 处理循环引用的问题
  if (map.has(originValue)) {
    return map.get(originValue);
  }

  // 判断传入的对象是数组还是对象
  const newObject = Array.isArray(originValue) ? [] : {};

  // 处理循环引用的问题
  map.set(originValue, newObject);

  for (const key in originValue) {
    newObject[key] = deepCopy(originValue[key], map);
  }

  // 对symbol的key进行特殊处理
  const symbolKeys = Object.getOwnPropertySymbols(originValue);
  for (const sKeys of symbolKeys) {
    newObject[sKeys] = deepCopy(originValue[sKeys], map);
  }

  return newObject;
}

异步控制并发数

function limitRequest(urls = [], limit = 3) {
  return new Promise((resolve, reject) => {
    const len = urls.length;
    let count = 0;

    // 同时启动limit个任务
    while (limit > 0) {
      start();
      limit -= 1;
    }

    function start() {
      const url = urls.shift(); // 从数组中拿取第一个任务
      if (url) {
        axios
          .post(url)
          .then((res) => {
            // todo
          })
          .catch((err) => {
            // todo
          })
          .finally(() => {
            if (count == len - 1) {
              // 最后一个任务完成
              resolve();
            } else {
              // 完成之后,启动下一个任务
              count++;
              start();
            }
          });
      }
    }
  });
}

Promise.all 的实现

Promise.mpAll = function (promiseList = []) {
  return new Promise((resolve, reject) => {
    const result = []; // 结果数组
    promiseList.forEach((p) => {
      p.then((res) => {
        result.push(res);
        if (result.length === promiseList.length) {
          resolve(result);
        }
      }).catch((e) => {
        reject(e);
      });
    });
  });
};

实现 instanceof 方法

function myInstanceof(obj, ctor) {
  let proto = Object.getPrototypeOf(obj);
  let prototype = ctor.prototype;
  while (true) {
    if (!proto) return false;
    if (proto === prototype) return true;
    proto = Object.getPrototypeOf(proto);
  }
}
人生の意味は平凡ですか、それとも素晴らしいですか?
最后更新于 2024-06-17