js手撕题

109 阅读2分钟

手撕深拷贝deepCopy&浅拷贝shallowCopy

公共方法

 // 判断数据类型
function types(v) {
  return Object.prototype.toString.call(v).slice(8, -1).toLowerCase();
}
// 是否是基础数据类型
function isBasisData(v) {
  return ["number", "string", "undefined", "null"].includes(types(v));
}

方法合集

function shallowCopy(v) {
  let newObj = Array.isArray(v) ? [] : {};
  if (isArr) v.forEach((e) => newObj.push(e));
  if (!isArr) Object.keys(v).forEach((e) => (newObj[e] = v[e]));
  return newObj;
}

/**
 * @param {*} v 原数组
 * @returns 拷贝完的新对象
 * 使用示例:deepCopy([1, 2, { a: {b:[3,4]} },[5,6]])
 */
function deepCopy(v) {
  let newObj = Array.isArray(v) ? [] : {};
  for (let i = 0; i < v.length; i++) {
    if (isBasisData(types(v[i]))) {
      newObj[i] = v[i];
    } else {
      newObj[i] = deepCopy(v[i]);
    }
  }

  return newObj;
}

// bubbleSort  冒泡(双重for循环)
function bubbleSort(val) {
  if (!Array.isArray(val)) return [];
  let v = shallowCopy(val);
  for (let i = 0; i < v.length; i++) {
    for (let j = 0; j < v.length; j++) {
      if (v[j] > v[j + 1]) {
        [v[j], v[j + 1]] = [v[j + 1], v[j]];
      }
    }
  }
  return v;
}

// quickSort  快排(基数化)
function quickSort(v) {
  if (v.length < 2) return v;
  let l = [],
    c = [],
    r = [],
    point = v[Math.floor(v.length / 2)];
  for (let i = 0; i < v.length; i++) {
    if (v[i] < point) {
      l.push(v[i]);
    } else if (v[i] > point) {
      r.push(v[i]);
    } else if (v[i] === point) {
      c.push(v[i]);
    }
  }
  return quickSort(l).concat(...c, ...quickSort(r));
}

/** 防抖(延迟执行,多次触发重新计算)
 * @param {*} fn 需要执行函数体
 * @param {*}  times 延迟时间
 * @param {*}  immediate 是否立即执行
 * @returns 执行函数
 * 使用示例: debounce(()=> {search()},props.throttleTime)
 */
function debounce(fn, times = 1000, immediate = false) {
  let timer = null; // 延迟器
  return function (...args) {
    if (immediate && !timer) {  // 立即执行
      fn.apply(this, args);
    }
    if (timer) clearTimeout(timer); // 延时器存在就清空
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, times);
  };
}

/** 节流(一定时间内多次触发只执行一次)
 * @param {*} fn 需要执行函数体
 * @param {*}  throttleTime 节流间隔时间
 * @returns 执行函数
 * 使用示例: throttle(()=> {search()},props.throttleTime)
 */
function throttle(fn, throttleTime = 1000) {
  let previous = 0; //老时间
  return function (...args) {
    let now = Date.now();
    if (now - previous > throttleTime) {
      previous = now; // 更新老时间
      fn.apply(this, args);
    } else {
      console.log("没到时间");
    }
  };
}

// call, apply, bind
// 添加自定义的call
Function.prototype.myCall = function (currentThis, ...agrs) {
  currentThis.myCall = this;
  currentThis.myCall(...agrs);
  delete currentThis.myCall;
};
// getA.myCall(obj,123,456)

// 添加自定义的apply
Function.prototype.myApply = function (currentThis, params) {
  currentThis.myApply = this;
  currentThis.myApply(params);
  delete currentThis.myApply;
};
// getA.myApply(obj,[123,456])

// 添加自定义的bind
Function.prototype.myBind = function (currentThis, ...agrs) {
  const fn = this;
  return function () {
    let thisArg = currentThis;
    thisArg.myBind = fn;
    fn.myCall(thisArg,...agrs);
    delete  thisArg.myBind;
  };
};
// getA.myBind(obj, 1, 2,4)()


/**
 * @param {*} custrogtor 构造函数 
 * @param {*}  args 参数
 * @returns obj 实例对象
 * 使用示例:myNew(Person,'hahah',12)
 */

function Person(name, age) {
  this.name = name;
  this.age = age;
}
// new 关键字源码实现
function myNew(custrogtor, ...args) {
  // 创建一个新对象,并指定原型
  let obj = Object.create(custrogtor.prototype);
  // 将函数 this 绑定到新对象
  custrogtor.apply(obj,args);
  // 返回新对象
  return obj;
}

/** 倒计时升级版倒计时: 解决 setInterval如果前面有阻塞线程的任务,那么就可能会导致 setInterval 函数延迟问题
 * requestAnimationFrame 允许以 60 帧/秒 (FPS) 的速率请求回调,而不会阻塞主线程。通过调用 requestAnimationFrame 方法浏览器会在下一次重绘之前执行指定的函数,这样可以确保回调在每一帧之间都能够得到适时的更新
 * @param {*} 需要倒计时时长 (ms)
 * @param {*}
 * 使用示例 example4(60000);
 */
function example4(leftTime) {
  let t = leftTime;
  let timeout = null;
  function start() {
    requestAnimationFrame(() => {
      t = t - 1000;
      if (t < 0) {
        timeout = null;
        console.log("倒计时结束",timeout);
        return;
      }
      timeout = setTimeout(() => {
        console.log(t);
        start();
      }, 1000);
    });
  }
  start();
}