js手写函数系列

147 阅读2分钟

浅拷贝:

function shallowClone(obj) {
  let cloneObj = {};
  for (let key in obj) {
    cloneObj[key] = obj[key];
  }
  return cloneObj;
}

深拷贝:

基础版(未考虑循环引用)

function deepClone(target) {
  if (!target || typeof target !== "object") {
    return target;
  }
  let result = Array.isArray(target) ? [] : {};
  for (let key in target) {
    result[key] = deepClone(target[key]);
  }
  return result;
}

注:如果为了性能考虑,可以将for... in 循环改为while 循环。

使用Map解决循环引用问题

Map类型可以使用object作为key值。使用Map类型保存每次克隆的对象,这样就不会出现子元素->父对象->子元素....的循环

function deepClone(target, map = new Map()) {
  if (!target || typeof target !== "object") {
    return target;
  }
  if (map.get(target)) {
    return map.get(target);
  }
  let result = Array.isArray(target) ? [] : {};
  map.set(target, result);
  for (let key in target) {
    result[key] = deepClone(target[key], map);
  }
  return result;
}

附 while 循环和 for... in 循环性能对比:

const whileLoop = (array) => {
  const start = Date.now();
  const length = array.length;
  let i = 0;
  let sum;
  while (i < length) {
    sum += array[i];
    i++;
  }
  console.info(Date.now() - start);
};

const forInLoop = (array) => {
  const start = Date.now();
  let sum;
  for (let key in array) {
    sum += array[key];
  }
  console.info(Date.now() - start);
};

const forLoop = (array) => {
  const start = Date.now();
  const length = array.length;
  let sum;
  for (let i = 0; i < length; i++) {
    sum += array[i];
  }
  console.info(Date.now() - start);
};

let testArray = Array.from({ length: 500000 }, (item, index) => index + 1);

whileLoop(testArray);
forInLoop(testArray);
forLoop(testArray);

微信截图_20210318195235.png

防抖

目的:降低同一事件触发的频率,减少浏览器性能消耗

效果:短时间内多次触发同一事件,事件处理函数只会在停止触发事件后执行一次

function debounce(fn, delay) {
  let timer;
  return function () {
    let _this = this;
    let args = arguments;
    if (timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(function () {
      fn.apply(_this, args);
    }, delay);
  };
}

节流

结果:短时间内多次触发同一事件,事件处理函数只会每隔一段时间执行一次

function throttle(fn, delay) {
  let timer;
  return function () {
    let _this = this;
    let args = arguments;
    if (timer) {
      return;
    }
    timer = setTimeout(function () {
      fn.apply(_this, args);
      timer = null;
    }, delay);
  };
}

call、apply、bind

call

思路主要是在要绑定this的对象上新建匿名属性,然后直接调用。

Function.prototype.myCall = function (obj, ...args) {
  const func = this;
  const tempFunc = Symbol("temp");
  obj[tempFunc] = func;
  const result = obj[tempFunc](...args);
  delete obj[tempFunc];
  return result;
};

apply和call仅args不同,不再重复

bind

Function.prototype.myBind = function (obj, ...args) {
  const func = this;
  const fbound = function (...otherArgs) {
    return func.call(new.target ? this : obj, ...args, ...otherArgs);
  };
  fbound.prototype = Object.create(func.prototype);
  fbound.prototype.constructor = fbound;
  return fbound;
};