likes
comments
collection
share

总结一些面试遇到的一些手撕题

作者站长头像
站长
· 阅读数 3

1. 手写 instanceof

instanceof 运算符用于判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。

步骤:

  1. 首先获取类型的原型
  2. 然后获得对象的原型
  3. 然后一直循环判断对象的原型是否等于类型的原型,直到对象原型为 null,因为原型链最终为 null

代码:

/**
 * 判断一个对象是否是另一个对象的实例
 * @param {*} left - 待判断的对象
 * @param {*} right - 参考的对象
 * @returns {boolean} - 如果是实例则返回true,否则返回false
 */
function myInstanceof(left, right) {
  // 如果left不是对象或者为null,则直接返回false
  if (typeof left !== "object" || left == null) {
    return false;
  }
  // 获取left的原型
  let proto = Object.getPrototypeOf(left);
  // 循环判断直到找到null为止
  while (true) {
    // 如果原型为null,则说明left不是right的实例,返回false
    if (proto === null) {
      return false;
    }
    // 如果原型为right的原型,则说明left是right的实例,返回true
    if (proto === right.prototype) {
      return true;
    }
    // 获取当前原型的原型
    proto = Object.getPrototypeOf(proto);
  }
}

2. 手写 new 操作符

步骤:

  1. 首先创建了一个新的空对象
  2. 设置原型,将对象的原型设置为函数的 prototype 对象。
  3. 让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)
  4. 判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。
function mynew(Func, ...args) {
  // 1.创建一个新对象
  // 2.新对象原型指向构造函数原型对象
  // 3.将构建函数的this指向新对象
  // 4.根据返回值判断
  let obj = {};
  obj._proto_ = Func.prototype;
  let res = Func.apply(obj, args);
  return res instanceof Object ? res : obj;
}

3. 手写 Promise

4. 手写防抖节流函数

4.1 防抖

函数防抖是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。

function debounce(fn, wait) {
  let timer = null; // 定义一个定时器变量
  return function (...args) {
    let _this = this; // 保存当前上下文
    if (timer) clearTimeout(timer); // 如果定时器存在,则清除定时器
    timer = setTimeout(function () {
      fn.apply(_this, args); // 调用原函数,并将参数传递给原函数
    }, wait); // 设置定时器,延迟执行原函数
  };
}

4.2 节流

函数节流是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。

function throttle(fn, wait) {
  let timer = undefined; // 定义一个定时器变量
  let _this = this; // 保存当前上下文
  return function (...args) {
    if (timer !== undefined) {
      return; // 如果定时器存在,则直接返回,不执行原函数
    } else {
      fn.apply(_this, args); // 调用原函数,并将参数传递给原函数
      timer = setTimeout(function () {
        timer = undefined; // 清除定时器
      }, wait); // 设置定时器,延迟执行清除定时器的操作
    }
  };
}

5. 手写 call,apply,bind 函数

5.1 call

// 为Function.prototype添加一个名为myCall的方法
Function.prototype.mycall = function (thisArg, ...args) {
  // 创建一个Symbol类型的key
  const key = Symbol("key");
  // 将this绑定到thisArg的key上
  thisArg[key] = this;
  // 调用thisArg的key方法,并传入args作为参数
  const res = thisArg[key](...args);
  // 删除thisArg的key
  delete thisArg[key];
  // 返回调用结果
  return res;
};

5.2 apply

// 1. 定义myapply方法
Function.prototype.myapply = function (thisArg, args) {
  // 使用Symbol作为key,用于存储函数
  const key = Symbol("key");
  // 将函数存储到thisArg对象中
  thisArg[key] = this;
  // 调用存储在thisArg对象中的函数,并传入args参数
  const res = thisArg[key](args);
  // 删除存储在thisArg对象中的函数
  delete thisArg[key];
  // 返回函数调用的结果
  return res;
};

5.3 bind

// bind 函数实现
Function.prototype.myBind = function (context) {
  // 判断调用对象是否为函数
  if (typeof this !== "function") {
    throw new TypeError("Error");
  }
  // 获取参数
  let args = [...arguments].slice(1);
  let fn = this;
  return function Fn() {
    // 根据调用方式,传入不同绑定值
    return fn.apply(
      this instanceof Fn ? this : context,
      args.concat(...arguments)
    );
  };
};

6 函数柯里化

函数柯里化指的是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。

function sumMarker(length) {
  let nums = []; // 存储累加的数字
  function sum(...args) {
    nums.push(...args); // 将传入的数字加入到累加数组中
    if (nums.length >= length) {
      // 当累加的数字个数达到指定长度时
      const res = nums.splice(0, length).reduce((pre, cur) => pre + cur, 0); // 从累加数组中取出指定长度的数字,进行累加,并返回累加结果
      nums = []; // 清空累加数组
      return res; // 返回累加结果
    } else {
      return sum; // 当累加的数字个数未达到指定长度时,返回自身,继续累加
    }
  }
  return sum; // 返回求和函数
}

总结

以上就是作者碰到的一些手撕题,如果你有补充的话欢迎在评论区讨论哦。