Skip to content

手写题

函数防抖

js
function debounce(fn, delay) {
  let timer;

  return function () {
    if (timer) {
      clearTimeout(timer);
    }

    timer = setTimeout(() => {
      fn.apply(this, arguments);
    }, delay);
  };
}

函数节流

js
function throttle(fn, wait) {
  let lastTime = 0;

  return function (...args) {
    const now = Date.now();
    if (now - lastTime >= wait) {
      fn.apply(this, args);
      lastTime = now;
    }
  };
}

数组去重

js
function unique(arr) {
  return [...new Set(arr)];
}

数组拍平

js
function flatten(arr) {
  return arr?.flat(Infinity);
}

数组乱序

js
function randomArr(arr) {
  return arr.sort(() => 0.5 - Math.random());
}

手写发布订阅

ts
type Handler = () => void;

class PubSub {
  private subscribers: Map<string, any>;

  constructor() {
    this.subscribers = new Map();
  }

  on(type: string, handler: Handler) {
    const handlers = this.subscribers.get(type);
    if (handlers) {
      this.subscribers.set(type, [...handlers, handler]);
    } else {
      this.subscribers.set(type, [handler]);
    }
  }

  off(type: string) {
    const handlers = this.subscribers.get(type);

    if (handlers) {
      this.subscribers.delete(type);
    }
  }

  emit(type: string, ...args: unknown[]) {
    const handlers = this.subscribers.get(type);

    if (handlers) {
      handlers.forEach((h) => h(...args));
    }
  }
}

深拷贝

  • 使用 JSON.parse 和 JSON.stringify 实现,但是会丢失 Function、underfind 和 null 值
js
function deepClone(obj) {
  return JSON.parse(JSON.stringify(obj));
}

另一种实现方法

js
function deepClone(obj) {
  if (obj === null || typeof obj !== "object") {
    return obj;
  }

  let result = Array.isArray(obj) ? [] : {};

  for (let key in obj) {
    if (Object.hasOwnProperty.call(obj, key)) {
      if (typeof obj[key] !== "object") {
        result[key] = obj[key];
      } else {
        result[key] = deepClone(obj[key]);
      }
    }
  }

  return result;
}

函数柯里化

JSON 转 Tree

js
// 数据
const students = [
  { id: 1, name: "ming", parentId: 0 },
  { id: 2, name: "hua", parentId: 1 },
  { id: 3, name: "qin", parentId: 1 },
  { id: 4, name: "liu", parentId: 2 },
];

function jsonToTree(data) {
  const map = new Map();

  data.forEach((item) => map.set(item.id, { ...item, children: [] }));

  let tree: any = [];

  data.forEach((item) => {
    if (item.parentId === 0) {
      tree.push(map.get(item.id));
    } else {
      const parent = map.get(item.parentId);

      if (parent) {
        parent.children.push(map.get(item.id));
      }
    }
  });

  return tree;
}

Tree 转 json

实现 Promise

call&apply&bind

实现 call 方法

js
Function.prototype.myCall = function (context, ...args) {
  context = context || globalThis;

  context["fn"] = this;
  const result = context["fn"](...args);
  delete context["fn"];

  return result;
};

实现 apply 方法

js
Function.prototype.myApply = function (context, args) {
  context = context || globalThis;

  context["fn"] = this;
  const result = context["fn"](...args);
  delete context["fn"];

  return result;
};

实现 bind 方法

js
Function.prototype.myBind = function (context, ...args) {
  return (...newArgs) => this.apply(context, [...args, ...newArgs]);
};

实现 instanceof 关键字

js
function myInstanceOf(left, right) {
  let prototype = right.prototype;
  left = left.__proto__;
  while (true) {
    if (!left) return false;
    if (left == prototype) return true;
    left = left.__proto__;
  }
}

实现 new 关键字

js
function myNew(fun, ...args) {
  let obj = {};
  obj.__proto__ = fun.prototype;
  let res = fun.apply(obj, args);
  return res instanceof Object ? res : obj;
}

实现 Object.assign

js
function myAssign(target, ...sources) {
  // 如果目标是 null 或 undefined,则抛出错误
  if (target == null) {
    throw new TypeError("Cannot convert undefined or null to object");
  }

  // 将 target 转换为对象
  target = Object(target);

  // 遍历每个源对象
  for (let i = 0; i < sources.length; i++) {
    const source = sources[i];

    // 如果 source 不是 null 或 undefined,才进行属性复制
    if (source != null) {
      // 获取源对象的所有可枚举属性
      for (let key of Object.keys(source)) {
        target[key] = source[key];
      }

      // 处理 Symbol 属性(因为 Object.keys() 不会获取到 Symbol 属性)
      if (Object.getOwnPropertySymbols) {
        const symbols = Object.getOwnPropertySymbols(source);
        for (let symbol of symbols) {
          target[symbol] = source[symbol];
        }
      }
    }
  }

  return target;
}

实现 sleep 方法

js
function sleep(delay) {
  return new Promise((resolve) => setTimeout(resolve, delay));
}

任务队列

js
class TaskQueue {
  constructor() {
    this.queue = []; // 存储任务
    this.running = false; // 是否正在执行任务
  }

  addTask(task) {
    this.queue.push(task);
    this.run(); // 每次添加任务后尝试执行
  }

  async run() {
    if (this.running) return;
    this.running = true;

    while (this.queue.length > 0) {
      const task = this.queue.shift();
      await task(); // 任务是一个异步函数
    }

    this.running = false;
  }
}

// 使用示例
const queue = new TaskQueue();

queue.addTask(
  () =>
    new Promise((res) =>
      setTimeout(() => {
        console.log("任务 1");
        res();
      }, 1000)
    )
);
queue.addTask(
  () =>
    new Promise((res) =>
      setTimeout(() => {
        console.log("任务 2");
        res();
      }, 500)
    )
);
queue.addTask(
  () =>
    new Promise((res) =>
      setTimeout(() => {
        console.log("任务 3");
        res();
      }, 800)
    )
);

console.log("任务队列已启动");

尾递归非波拉切数列

js
function fibonacciTail(n, a = 0, b = 1) {
  if (n === 0) return a;
  if (n === 1) return b;
  return fibonacciTail(n - 1, b, a + b);
}

// 测试
console.log(fibonacciTail(10)); // 55
console.log(fibonacciTail(50)); // 12586269025

实现 LazyMan

setTimout 实现 setInterval

js
function mySetinterval(fn, delay) {
  let timer;

  function loop() {
    timer = window.setTimeout(() => {
      fn();
      loop();
    }, delay);
  }

  loop();

  return {
    clear: () => clearTimeout(timer),
  };
}

实现 compose 方法

js
function compose(...fns) {
  return function (initialValue) {
    return fns.reduceRight((acc, fn) => fn(acc), initialValue);
  };
}

两个数组的交集

js
function intersection(arr1, arr2) {
  return [...new Set(arr1)].filter((item) => arr2.includes(item));
}

实现对象数组去重

js
function uniqueById(arr, key) {
  const map = new Map();

  return arr.filter((item) => {
    if (!map.has(item[key])) {
      map.set(item[key], item);
      return true;
    }
    return false;
  });
}

实现类数组转换为数组方法

js
function toArray(arrayLike) {
  return Array.from(arrayLike);
}

实现JS函数记忆

实现异步并发数限制

实现判断数据类型的方法

js
function getType(value) {
  return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
}