手撕复习

57 阅读3分钟

防抖节流

/**
 * 防抖(简单版)
 * @param {Function} fn 
 * @param {Number} [delay 
 * @returns 
 */
function debounce(fn, delay) {
  let timer = null;
  return function (...args) {
    if (timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
}
/**
 * 防抖(优化版)
 * @param {Function} fn 需要防抖处理的函数
 * @param {Number} time 防抖时间
 * @param {Boolean} triggleNow 是否立即触发
 * @returns {*} fn执行结果 
 */
function debounce(fn, time, triggleNow) {
  let timer = null;
  return function () {
    let _self = this,
      args = arguments,
      result = null;
    if (timer) {
      clearTimeout(timer);
    }
    if (triggleNow) {
      /**
       * 1. 如果第一次进来timer为null,直接执行回调
       * 2. 然后给timer赋值,过了防抖时间将timer再次置null
       * 3. 如果在防抖时间内再次触发了函数,clearTimeout会取消上一个将timer置null的操作,timer就有值了
       * 4. 只有timer为null时才会执行函数fn
      */
      const exec = !timer;
      timer = setTimeout(() => {
        timer = null;
      }, time);
      if (exec) {
        result = fn.apply(_self, args);
      }
    } else {
      timer = setTimeout(() => {
        result = fn.apply(_self, args);
      }, time);
    }
    return result;
  }
}
/**
 * 节流(简单版)
 * @param {Function} fn 
 * @param {Number} delay 
 * @returns 
 */
function throttle(fn, delay) {
  let timer = null;
  return function (...args) {
    if (!timer) {
      timer = setTimeout(() => {
        timer = null;
        fn.apply(this, args);
      }, delay);
    }
  };
}
/**
 * 节流(优化版)
 * @param {Function} fn 需要节流处理的函数
 * @param {Number} delay 节流时间
 * @param {Boolean} needLast 是否执行最后一次触发
 * @returns fn执行结果
 */
function throttle (fn, delay, needLast) {
  let timer = null,
    beginTime = new Date().getTime();
  return function () {
    let _self = this,
      args = arguments,
      curTime = new Date().getTime(),
      result = null;
    clearTimeout(timer)
    //判断两次间隔如果大于设置时间执行函数
    if (curTime - beginTime >= delay) {
      result = fn.apply(_self, args);
      beginTime = curTime;
    } else {
      //如果设置需要执行最后一次触发,那么就利用setTimeout延迟后执行
      if (needLast) {
        timer = setTimeout(() => {
          result = fn.apply(_self, args);
        }, delay);
      }
    }
    return result
  }
}

new

// 构造函数
function Person(name, age) {
  this.name = name;
  this.age = age;
}

/**
 * 模拟 new 函数
 * @param {Function} ctor 
 * @param  {...any} args 
 * @returns 
 */
function create(ctor, ...args) {
  if (typeof ctor !== 'function') {
    throw 'constructor error'
  }
  // create()方法的实现中使用了new操作符
  // let newObj = Object.create(ctor.prototype);
  let newObj = {};
  newObj._proto_ = ctor.prototype;
  ctor.call(newObj, ...args);
  return newObj;
}

// 测试用例
let obj = create(Person, 'jzy', 18);
console.log('createObj', obj);
let obj2 = new Person('jzy', 18);
console.log('newObj', obj2);

call 、 apply

image.png

Function.prototype.myCall = function (context, ...args) {
  // 如果this为null或undefined,则指向window对象
  context = context ? Object(context) : window;
  // 声明一个symbol类型当key,以防原对象中存在该属性而导致覆盖问题
  const key = Symbol();
  // 这里的this是指向调用函数,并把该函数绑定在指定对象(context)上
  context[key] = this;
  // 在指定上下文上传入参数并执行函数
  const result = context[key](...args);
  // 把函数从指定的对象上删除
  delete context[key];
  return result;
};
Function.prototype.myApply = function (context, args) {
  // 如果this为null或undefined,则指向window对象
  context = context ? Object(context) : window;
  // 声明一个symbol类型当key,以防原对象中存在该属性而导致覆盖问题
  const key = Symbol();
  // 这里的this是指向调用函数,并把该函数绑定在指定对象(context)上
  context[key] = this;
  // 在指定上下文上传入参数并执行函数
  const result = args ? context[key](...args) : context[key]();
  // 把函数从指定的对象上删除
  delete context[key];
  return result;
};

bind

image.png

Function.prototype.myBind = function (context, ...outArgs) {
  context = context || {};
  let that = this;
  // 返回一个函数
  let fn = function (...innerArgs) {
    // 如果constructor即res函数的prototype出现在调用对象(this)的原型链上,即res被当作构造函数了
    if (this instanceof fn) {
      // 针对new时的特殊处理,把执行上下文放在new中
      return that.call(this, ...outArgs, ...innerArgs);
    } else {
      return that.call(context, ...outArgs, ...innerArgs);
    }
  };
  fn.prototype = this.prototype;
  return fn1;
};

深拷贝

/**
 * 深拷贝(利用weakMap解决循环引用问题)
 * @param {*} obj
 * @returns
 */
function deepClone(obj, map = new WeakMap()) {
  // 深克隆只存在于{},[]
  let cloneObj;
  if (typeof obj !== "object") return obj;
  if (map.has(obj)) return map.get(obj);
  cloneObj = new obj.constructor();
  map.set(obj, cloneObj);
  //  循环递归
  for (let key in obj) {
    cloneObj[key] = deepClone(obj[key]);
  }
  return cloneObj;
}