Skip to content

Latest commit

 

History

History
750 lines (612 loc) · 14.8 KB

手写题.md

File metadata and controls

750 lines (612 loc) · 14.8 KB

手写题

函数防抖

function debounce(fn, delay) {
  let timer;

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

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

函数节流

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

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

数组去重

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

数组拍平

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

数组乱序

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

手写发布订阅

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 值
function deepClone(obj) {
  return JSON.parse(JSON.stringify(obj));
}

另一种实现方法

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;
}

函数柯里化

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn(...args);
    } else {
      return (...nextArgs) => curried(...args, ...nextArgs);
    }
  };
}

JSON 转 Tree

// 数据
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

function treeToList(tree, parentId = null, result = []) {
  tree.forEach((node) => {
    const { id, name, children } = node;
    result.push({ id, name, parentId });

    if (children && children.length) {
      treeToList(children, id, result);
    }
  });

  return result;
}

// 示例数据
const treeData = [
  {
    id: 1,
    name: "A",
    children: [
      {
        id: 2,
        name: "B",
        children: [
          { id: 4, name: "D", children: [] },
          { id: 5, name: "E", children: [] },
        ],
      },
      {
        id: 3,
        name: "C",
        children: [{ id: 6, name: "F", children: [] }],
      },
    ],
  },
];

console.log(JSON.stringify(treeToList(treeData), null, 2));

实现 Promise

Promise A+ 规范核心

  1. Promise 具有 then 方法,并支持 链式调用。
  2. then 方法接收两个参数(onFulfilled 和 onRejected)。
  3. Promise 必须是 异步执行(基于 setTimeout 实现 microtask)。
  4. Promise 支持 状态管理,包括:
  • pending(等待)
  • fulfilled(成功)
  • rejected(失败)
  • 状态 一旦改变,不可逆。
  1. then 必须返回一个新的 Promise,支持 值传递、异常处理、嵌套 Promise 解析。
class MyPromise {
  constructor(executor) {
    this.state = "pending"; // 初始状态
    this.value = undefined; // 成功返回值
    this.reason = undefined; // 失败原因
    this.onFulfilledCallbacks = []; // 存储成功回调
    this.onRejectedCallbacks = []; // 存储失败回调

    const resolve = (value) => {
      if (this.state === "pending") {
        setTimeout(() => {
          // 确保异步执行
          this.state = "fulfilled";
          this.value = value;
          this.onFulfilledCallbacks.forEach((callback) => callback(value));
        });
      }
    };

    const reject = (reason) => {
      if (this.state === "pending") {
        setTimeout(() => {
          this.state = "rejected";
          this.reason = reason;
          this.onRejectedCallbacks.forEach((callback) => callback(reason));
        });
      }
    };

    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  then(onFulfilled, onRejected) {
    // 处理 onFulfilled/onRejected 为空的情况
    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : (value) => value;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (reason) => {
            throw reason;
          };

    return new MyPromise((resolve, reject) => {
      const handle = (callback, value, resolver) => {
        try {
          const result = callback(value);
          if (result instanceof MyPromise) {
            result.then(resolve, reject); // 递归解析 Promise
          } else {
            resolve(result);
          }
        } catch (error) {
          reject(error);
        }
      };

      if (this.state === "fulfilled") {
        setTimeout(() => handle(onFulfilled, this.value, resolve));
      } else if (this.state === "rejected") {
        setTimeout(() => handle(onRejected, this.reason, reject));
      } else {
        this.onFulfilledCallbacks.push(() =>
          handle(onFulfilled, this.value, resolve)
        );
        this.onRejectedCallbacks.push(() =>
          handle(onRejected, this.reason, reject)
        );
      }
    });
  }

  catch(onRejected) {
    return this.then(null, onRejected);
  }

  finally(callback) {
    return this.then(
      (value) => MyPromise.resolve(callback()).then(() => value),
      (reason) =>
        MyPromise.resolve(callback()).then(() => {
          throw reason;
        })
    );
  }

  // 静态 resolve
  static resolve(value) {
    return new MyPromise((resolve) => resolve(value));
  }

  // 静态 reject
  static reject(reason) {
    return new MyPromise((_, reject) => reject(reason));
  }

  // 静态 all
  static all(promises) {
    return new MyPromise((resolve, reject) => {
      let count = 0;
      const results = [];
      promises.forEach((promise, index) => {
        MyPromise.resolve(promise).then((value) => {
          results[index] = value;
          if (++count === promises.length) resolve(results);
        }, reject);
      });
    });
  }

  // 静态 race
  static race(promises) {
    return new MyPromise((resolve, reject) => {
      promises.forEach((promise) =>
        MyPromise.resolve(promise).then(resolve, reject)
      );
    });
  }
}

call&apply&bind

实现 call 方法

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

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

  return result;
};

实现 apply 方法

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

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

  return result;
};

实现 bind 方法

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

实现 instanceof 关键字

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 关键字

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

实现 Object.assign

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 方法

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

任务队列

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("任务队列已启动");

尾递归非波拉切数列

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

class LazyManClass {
  constructor(name) {
    this.name = name;
    this.tasks = [];

    // 任务队列初始化时,先插入 sayHi 任务
    this.tasks.push(() => {
      console.log(`Hi, I am ${this.name}`);
      this.next();
    });

    // 确保任务开始执行
    setTimeout(() => this.next(), 0);
  }

  // 执行下一个任务
  next() {
    if (this.tasks.length > 0) {
      const task = this.tasks.shift();
      task();
    }
  }

  // 吃饭任务
  eat(food) {
    this.tasks.push(() => {
      console.log(`Eating ${food}`);
      this.next();
    });
    return this;
  }

  // 睡眠任务
  sleep(time) {
    this.tasks.push(() => {
      setTimeout(() => {
        console.log(`Wake up after ${time} seconds`);
        this.next();
      }, time * 1000);
    });
    return this;
  }

  // 优先睡眠(插入到任务队列最前面)
  sleepFirst(time) {
    this.tasks.unshift(() => {
      setTimeout(() => {
        console.log(`Wake up after ${time} seconds`);
        this.next();
      }, time * 1000);
    });
    return this;
  }
}

// 包装成函数,返回 LazyManClass 实例
function LazyMan(name) {
  return new LazyManClass(name);
}

// 测试用例
LazyMan("Tony").eat("breakfast").sleep(2).eat("lunch");
LazyMan("Tony").sleepFirst(2).eat("lunch");

setTimout 实现 setInterval

function mySetinterval(fn, delay) {
  let timer;

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

  loop();

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

实现 compose 方法

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

两个数组的交集

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

实现对象数组去重

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;
  });
}

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

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

实现 JS 函数记忆

函数记忆化(Memoization) 是一种优化技术,主要用于缓存函数的计算结果,避免重复计算,提升性能。适用于纯函数(相同输入始终返回相同输出)。

function memoize(fn) {
  const cache = new Map(); // 存储计算结果

  return function (...args) {
    const key = JSON.stringify(args); // 用参数序列化作为缓存键
    if (cache.has(key)) {
      console.log("Fetching from cache:", key);
      return cache.get(key);
    }
    console.log("Computing result:", key);
    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
}

实现异步并发数限制

在 JavaScript 中,Promise.all 会并行执行所有异步任务,但如果任务量很大,会导致资源占用过高,因此需要限制并发数。

function limitConcurrency(tasks, limit) {
  return new Promise((resolve, reject) => {
    let index = 0; // 任务索引
    let running = 0; // 当前运行中的任务数
    const results = []; // 存储结果

    function runNext() {
      if (index >= tasks.length && running === 0) {
        resolve(results); // 所有任务完成
        return;
      }
      if (running >= limit || index >= tasks.length) return; // 达到并发限制

      const taskIndex = index;
      const task = tasks[index++];
      running++;

      task()
        .then((result) => {
          results[taskIndex] = result;
        })
        .catch((error) => {
          results[taskIndex] = error; // 捕获错误,防止中断
        })
        .finally(() => {
          running--;
          runNext(); // 继续下一个任务
        });

      runNext(); // 继续分配任务
    }

    runNext(); // 启动任务
  });
}

实现判断数据类型的方法

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