【跳槽必备】2023常用手写面试题知识点总结(一)

简介: 【跳槽必备】2023常用手写面试题知识点总结(一)

前言

想想几年前一个月随便投出去一天至少3面试一个月排满面试的场景对于现在已经灭绝了,基本只有外包和驻场有回应,今年很多人都只能猥琐发育,市场上不仅岗位变少,money也少了很多。目前环境的不景气,面试难度也增加了很多,在这样的大环境下也只能不断提升提升自己,时刻警惕被优化的风险。最近刚好复习到手写面试题的基础分享分享给大家伙。

手写实现节流函数

function throttle(func, delay) {
  let timeoutId; // 用于存储定时器的ID
  let lastExecutedTime = 0; // 上次执行的时间戳
  // 返回一个新的函数作为节流函数
  return function (...args) {
    const currentTime = Date.now(); // 当前时间戳
    // 计算距离上次执行的时间间隔
    const elapsedTime = currentTime - lastExecutedTime;
    // 如果距离上次执行的时间间隔大于等于延迟时间,则执行函数
    if (elapsedTime >= delay) {
      func.apply(this, args);
      lastExecutedTime = currentTime; // 更新上次执行的时间戳
    } else {
      // 如果距离上次执行的时间间隔小于延迟时间,则设置定时器延迟执行函数
      clearTimeout(timeoutId);
      timeoutId = setTimeout(() => {
        func.apply(this, args);
        lastExecutedTime = Date.now(); // 更新上次执行的时间戳
      }, delay - elapsedTime);
    }
  };
}

该实现中,throttle 函数接收两个参数:func 是要进行节流处理的函数,delay 是时间间隔,表示在间隔时间内只能执行一次函数。


节流函数返回一个新的函数作为节流后的函数。每次调用节流后的函数时,它会记录当前时间戳,并计算距离上次执行的时间间隔。


如果距离上次执行的时间间隔大于等于延迟时间,则立即执行目标函数,并更新上次执行的时间戳。


如果距离上次执行的时间间隔小于延迟时间,则设置一个定时器,在延迟时间减去时间间隔后执行目标函数,并更新上次执行的时间戳。


这样,可以确保目标函数在一定时间间隔内只能执行一次。


示例,演示如何使用节流函数:

function handleResize() {
  console.log('Resize event throttled');
}
const throttledResize = throttle(handleResize, 200);
window.addEventListener('resize', throttledResize);

在示例中,我们定义了一个 handleResize 函数,并使用 throttle 函数对其进行节流处理。然后,我们将节流后的函数 throttledResize 添加到 resize 事件的监听器中。这样,当窗口大小调整时,handleResize 函数只会在每 200 毫秒执行一次,从而减少了函数的执行频率。

手写实现防抖函数

function debounce(func, delay) {
  let timeoutId; // 用于存储定时器的ID
  // 返回一个新的函数作为防抖函数
  return function (...args) {
    // 如果已经设置了定时器,则清除它
    if (timeoutId) {
      clearTimeout(timeoutId);
    }
    // 设置新的定时器,延迟执行目标函数
    timeoutId = setTimeout(() => {
      func.apply(this, args);
    }, delay);
  };
}

该实现中,debounce 函数接收两个参数:func 是要进行防抖处理的函数,delay 是延迟时间,表示在最后一次触发后等待多久执行函数。


防抖函数返回一个新的函数作为防抖后的函数。每次调用防抖后的函数时,它会判断是否已经设置了定时器。如果已经设置了定时器,则清除之前的定时器,以防止函数执行。然后,设置一个新的定时器,延迟执行目标函数。


当最后一次调用防抖后的函数后,如果在延迟时间内没有再次调用,则定时器会触发,执行目标函数 func。这样,可以确保目标函数只在最后一次调用后延迟一段时间执行。


示例,演示如何使用防抖函数:

function handleScroll() {
  console.log('Scroll event debounced');
}
const debouncedScroll = debounce(handleScroll, 200);
window.addEventListener('scroll', debouncedScroll);

在示例中,我们定义了一个 handleScroll 函数,并使用 debounce 函数对其进行防抖处理。然后,我们将防抖后的函数 debouncedScroll 添加到 scroll 事件的监听器中。这样,当页面滚动时,handleScroll 函数只会在最后一次滚动后的 200 毫秒延迟执行,从而减少了函数的执行次数。

函数柯里化的实现

function curry(fn) {
  // 定义内部函数用于递归调用
  function curried(...args) {
    // 如果传入的参数数量大于或等于原始函数的参数数量,则直接调用原始函数
    if (args.length >= fn.length) {
      return fn(...args);
    } else {
      // 如果参数数量不足,则返回一个新的函数,继续等待接收剩余的参数
      return function (...moreArgs) {
        return curried(...args, ...moreArgs);
      };
    }
  }
  return curried; // 返回柯里化后的函数
}

手写实现new操作符

// 自定义new操作符函数:模拟实现new操作符的功能
function myNew(constructorFn, ...args) {
  // 创建一个空对象,并将其原型设置为构造函数的原型
  const obj = Object.create(constructorFn.prototype);
  // 调用构造函数,并将this绑定到新创建的对象上
  const result = constructorFn.apply(obj, args);
  // 如果构造函数返回一个对象,则返回该对象;否则返回新创建的对象
  if (typeof result === 'object' && result !== null) {
    return result;
  }
  return obj;
}
// 示例构造函数
function Person(name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.sayHello = function() {
  console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
};
// 使用示例
const john = myNew(Person, 'John', 25);
john.sayHello(); // Output: Hello, my name is John and I'm 25 years old.

手写实现instanceof

// 自定义instanceof操作符函数:模拟实现instanceof操作符的功能
function myInstanceOf(obj, constructorFn) {
  // 获取构造函数的原型对象
  const prototype = constructorFn.prototype;
  // 判断对象的原型链中是否存在构造函数的原型
  while (obj !== null) {
    if (obj.__proto__ === prototype) {
      return true;
    }
    obj = obj.__proto__;
  }
  return false;
}
// 示例构造函数
function Person(name, age) {
  this.name = name;
  this.age = age;
}
// 使用示例
const john = new Person('John', 25);
console.log(myInstanceOf(john, Person)); // Output: true
console.log(myInstanceOf(john, Object)); // Output: true
console.log(myInstanceOf(john, Array)); // Output: false

手写实现Promise:

// 自定义Promise类:模拟实现Promise的基本功能
class MyPromise {
  constructor(executor) {
    // Promise的三个状态:pending、fulfilled、rejected
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    // 用于存储异步操作的回调函数
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    // 执行executor函数
    try {
      executor(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
      this.reject(error);
    }
  }
  // 解决Promise(状态变为fulfilled)
  resolve(value) {
    if (this.state === 'pending') {
      this.state = 'fulfilled';
      this.value = value;
      // 执行所有已注册的fulfilled回调函数
      this.onFulfilledCallbacks.forEach(callback => callback(this.value));
    }
  }
  // 拒绝Promise(状态变为rejected)
  reject(reason) {
    if (this.state === 'pending') {
      this.state = 'rejected';
      this.reason = reason;
      // 执行所有已注册的rejected回调函数
      this.onRejectedCallbacks.forEach(callback => callback(this.reason));
    }
  }
  // 注册Promise的回调函数
  then(onFulfilled, onRejected) {
    if (this.state === 'fulfilled') {
      onFulfilled(this.value);
    } else if (this.state === 'rejected') {
      onRejected(this.reason);
    } else if (this.state === 'pending') {
      // 异步操作时,将回调函数存储起来,待异步操作完成后执行
      this.onFulfilledCallbacks.push(onFulfilled);
      this.onRejectedCallbacks.push(onRejected);
    }
    // 返回新的Promise对象,实现链式调用
    return this;
  }
}
// 使用示例
const promise = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('Hello, Promise!');
  }, 2000);
});
promise.then(value => {
  console.log(value); // Output: Hello, Promise!
});

promise.all()的实现

Promise.all 方法用于接收一个 Promise 数组,并在所有 Promise 都成功解析时返回一个包含所有 Promise 结果的新 Promise。如果任何一个 Promise 失败,则返回的 Promise 将立即被拒绝,并带有失败的原因。

function promiseAll(promises) {
  return new Promise((resolve, reject) => {
    const results = []; // 用于保存每个Promise的结果
    let completedPromises = 0; // 已经完成的Promise数量
    // 遍历传入的Promise数组
    for (let i = 0; i < promises.length; i++) {
      // 对每个Promise进行处理
      promises[i]
        .then((result) => {
          // 如果Promise成功解决,则将结果保存在相应的位置
          results[i] = result;
          completedPromises++;
          // 当所有Promise都完成时,返回结果
          if (completedPromises === promises.length) {
            resolve(results);
          }
        })
        .catch((error) => {
          // 如果有一个Promise被拒绝,则立即返回拒绝的结果
          reject(error);
        });
    }
    // 处理空Promise数组的情况
    if (promises.length === 0) {
      resolve(results);
    }
  });
}

promise.race的实现

Promise.race方法接收一个包含多个Promise的可迭代对象(如数组),并返回一个新的Promise。这个新的Promise将与第一个解决(resolve)或拒绝(reject)的Promise具有相同的解决值或拒绝原因。

promise.race的实现function promiseRace(promises) {
  return new Promise((resolve, reject) => {
    // 遍历传入的Promise数组
    for (const promise of promises) {
      // 对每个Promise进行处理
      promise
        .then((result) => {
          // 如果有一个Promise解决,则使用resolve解决新的Promise
          resolve(result);
        })
        .catch((error) => {
          // 如果有一个Promise被拒绝,则使用reject拒绝新的Promise
          reject(error);
        });
    }
  });
}

Promise.allSettled的实现

Promise.allSettled 方法接收一个包含多个 Promise 的可迭代对象(如数组),并返回一个新的 Promise。这个新的 Promise 在所有传入的 Promise 都解决或拒绝后才会被解决,并返回一个包含每个 Promise 结果的对象数组。对象数组中的每个对象都表示对应的 Promise 结果,包含 status 字段表示 Promise 的状态(“fulfilled” 表示解决,“rejected” 表示拒绝),以及对应的 value 或 reason 字段表示解决值或拒绝原因。

function promiseAllSettled(promises) {
  const settledPromises = [];
  // 遍历传入的 Promise 数组
  for (const promise of promises) {
    // 对每个 Promise 进行处理
    settledPromises.push(
      Promise.resolve(promise)
        .then((value) => ({
          status: 'fulfilled',
          value: value,
        }))
        .catch((reason) => ({
          status: 'rejected',
          reason: reason,
        }))
    );
  }
  return Promise.all(settledPromises);
}

手写实现apply函数:

// 自定义apply函数:模拟实现apply的功能
function myApply(fn, context, args) {
  if (typeof fn !== 'function') {
    throw new TypeError('fn must be a function');
  }
  // 创建唯一的属性名,用于防止上下文对象属性的覆盖
  const uniqueKey = Symbol();
  // 将函数fn作为上下文对象的一个属性
  context[uniqueKey] = fn;
  // 调用函数fn,并传入参数数组
  const result = context[uniqueKey](...args);
  // 删除添加的属性
  delete context[uniqueKey];
  return result;
}
// 使用示例
function greet(greeting) {
  return `${greeting}, ${this.name}!`;
}
const person = { name: 'John' };
const greeting = myApply(greet, person, ['Hello']);
console.log(greeting); // Output: Hello, John!

手写实现call函数:

// 自定义call函数:模拟实现call的功能
function myCall(fn, context, ...args) {
  if (typeof fn !== 'function') {
    throw new TypeError('fn must be a function');
  }
  // 创建唯一的属性名,用于防止上下文对象属性的覆盖
  const uniqueKey = Symbol();
  // 将函数fn作为上下文对象的一个属性
  context[uniqueKey] = fn;
  // 调用函数fn,并传入参数列表
  const result = context[uniqueKey](...args);
  // 删除添加的属性
  delete context[uniqueKey];
  return result;
}
// 使用示例
function greet(greeting) {
  return `${greeting}, ${this.name}!`;
}
const person = { name: 'John' };
const greeting = myCall(greet, person, 'Hello');
console.log(greeting); // Output: Hello, John!

手写实现bind函数:

// 自定义bind函数:模拟实现bind的功能
function myBind(fn, context, ...args) {
  if (typeof fn !== 'function') {
    throw new TypeError('fn must be a function');
  }
  return function (...newArgs) {
    // 将bind时传入的参数和调用时传入的参数合并
    const allArgs = [...args, ...newArgs];
    // 调用函数fn,并绑定上下文和参数列表
    return fn.apply(context, allArgs);
  };
}
// 使用示例
function greet(greeting) {
  return `${greeting}, ${this.name}!`;
}
const person = { name: 'John' };
const greetPerson = myBind(greet, person, 'Hello');
const greeting = greetPerson();
console.log(greeting); // Output: Hello, John!

手写实现深拷贝函数:

function deepClone(obj, hash = new WeakMap()) {
  if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof RegExp) return new RegExp(obj);
  // 可能是对象或者普通的值  如果是函数的话是不需要深拷贝
  if (typeof obj !== "object") return obj;
  // 是对象的话就要进行深拷贝
  if (hash.get(obj)) return hash.get(obj);
  let cloneObj = new obj.constructor();
  // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
  hash.set(obj, cloneObj);
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      // 实现一个递归拷贝
      cloneObj[key] = deepClone(obj[key], hash);
    }
  }
  return cloneObj;
}
let obj = { name: 1, address: { x: 100 } };
obj.o = obj; // 对象存在循环引用的情况
let d = deepClone(obj);
obj.address.x = 200;
console.log(d);
相关文章
|
6天前
|
Android开发
Android面试高频知识点(1) 图解Android事件分发机制
Android面试高频知识点(1) 图解Android事件分发机制
|
6天前
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
|
6天前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
|
8天前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
Android面试高频知识点(4) 详解Activity的启动流程
16 3
|
9天前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
15 2
|
9天前
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
30 1
|
9天前
|
Android开发
Android面试高频知识点(1) 图解 Android 事件分发机制
Android面试高频知识点(1) 图解 Android 事件分发机制
23 1
|
14天前
|
XML 前端开发 Android开发
Android面试高频知识点(1) 图解Android事件分发机制
Android面试高频知识点(1) 图解Android事件分发机制
Android面试高频知识点(1) 图解Android事件分发机制
|
2月前
|
Web App开发 前端开发 Linux
「offer来了」浅谈前端面试中开发环境常考知识点
该文章归纳了前端开发环境中常见的面试知识点,特别是围绕Git的使用进行了详细介绍,包括Git的基本概念、常用命令以及在团队协作中的最佳实践,同时还涉及了Chrome调试工具和Linux命令行的基础操作。
「offer来了」浅谈前端面试中开发环境常考知识点
|
4月前
|
存储 算法 安全
Java面试题:Java内存模型及相关知识点深度解析,Java虚拟机的内存结构及各部分作用,详解Java的垃圾回收机制,谈谈你对Java内存溢出(OutOfMemoryError)的理解?
Java面试题:Java内存模型及相关知识点深度解析,Java虚拟机的内存结构及各部分作用,详解Java的垃圾回收机制,谈谈你对Java内存溢出(OutOfMemoryError)的理解?
65 0