前端面试常见 20 道手写题(含答案)

161 阅读2分钟

一、函数相关

1. 手写 call

Function.prototype.myCall = function (context, ...args) {
  context = context || window;
  const fn = Symbol('fn');
  context[fn] = this;
  const res = context[fn](...args);
  delete context[fn];
  return res;
};

2. 手写 apply

Function.prototype.myApply = function (context, args) {
  context = context || window;
  const fn = Symbol('fn');
  context[fn] = this;
  const res = args ? context[fn](...args) : context[fn]();
  delete context[fn];
  return res;
};

3. 手写 bind

Function.prototype.myBind = function (context, ...args) {
  const fn = this;
  return function (...rest) {
    return fn.apply(context, [...args, ...rest]);
  };
};

4. 手写 new

function myNew(Constructor, ...args) {
  const obj = Object.create(Constructor.prototype);
  const res = Constructor.apply(obj, args);
  return typeof res === 'object' && res !== null ? res : obj;
}

5. 手写柯里化 curry

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return (...next) => curried.apply(this, [...args, ...next]);
    }
  };
}

二、防抖 & 节流

6. 手写防抖 debounce

function debounce(fn, delay) {
  let timer = null;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

7. 手写节流 throttle

function throttle(fn, delay) {
  let last = 0;
  return function (...args) {
    const now = Date.now();
    if (now - last > delay) {
      fn.apply(this, args);
      last = now;
    }
  };
}

三、对象 & 数组

8. 手写深拷贝 deepClone

function deepClone(obj, map = new WeakMap()) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (map.has(obj)) return map.get(obj);
  const res = Array.isArray(obj) ? [] : {};
  map.set(obj, res);
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      res[key] = deepClone(obj[key], map);
    }
  }
  return res;
}

9. 手写 instanceof

function myInstanceof(obj, Constructor) {
  let proto = Object.getPrototypeOf(obj);
  while (proto) {
    if (proto === Constructor.prototype) return true;
    proto = Object.getPrototypeOf(proto);
  }
  return false;
}

10. 手写 Object.create

function myCreate(proto) {
  function F() {}
  F.prototype = proto;
  return new F();
}

11. 手写数组扁平化 flat

function flat(arr) {
  return arr.reduce((acc, cur) => acc.concat(Array.isArray(cur) ? flat(cur) : cur), []);
}

12. 手写数组去重

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

四、Promise 相关

13. 手写简易版 Promise

class MyPromise {
  constructor(executor) {
    this.state = 'pending';
    this.value = null;
    this.callbacks = [];

    const resolve = (value) => {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        this.callbacks.forEach(cb => cb(value));
      }
    };

    executor(resolve);
  }

  then(onFulfilled) {
    if (this.state === 'fulfilled') {
      onFulfilled(this.value);
    } else {
      this.callbacks.push(onFulfilled);
    }
  }
}

14. 手写 Promise.all

Promise.myAll = function (promises) {
  return new Promise((resolve, reject) => {
    const res = [];
    let count = 0;
    promises.forEach((p, i) => {
      Promise.resolve(p).then(val => {
        res[i] = val;
        count++;
        if (count === promises.length) resolve(res);
      }, reject);
    });
  });
};

15. 手写 Promise.race

Promise.myRace = function (promises) {
  return new Promise((resolve, reject) => {
    promises.forEach(p => Promise.resolve(p).then(resolve, reject));
  });
};

五、工具类

16. 手写事件发布订阅 EventEmitter

class EventEmitter {
  constructor() {
    this.events = {};
  }
  on(event, fn) {
    (this.events[event] = this.events[event] || []).push(fn);
  }
  emit(event, ...args) {
    this.events[event]?.forEach(fn => fn(...args));
  }
  off(event, fn) {
    this.events[event] = this.events[event]?.filter(f => f !== fn);
  }
}

17. 手写 JSON.stringify(简化版)

function myStringify(obj) {
  if (obj === null) return "null";
  if (typeof obj === "string") return `"${obj}"`;
  if (typeof obj === "object") {
    if (Array.isArray(obj)) {
      return `[${obj.map(myStringify).join(",")}]`;
    }
    return `{${Object.keys(obj).map(k => `"${k}":${myStringify(obj[k])}`).join(",")}}`;
  }
  return String(obj);
}

18. 手写 JSON.parse(简化版)

function myParse(str) {
  return eval('(' + str + ')');
}

19. 手写 LRU 缓存

class LRUCache {
  constructor(limit) {
    this.limit = limit;
    this.map = new Map();
  }
  get(key) {
    if (!this.map.has(key)) return -1;
    const val = this.map.get(key);
    this.map.delete(key);
    this.map.set(key, val);
    return val;
  }
  put(key, val) {
    if (this.map.has(key)) this.map.delete(key);
    if (this.map.size >= this.limit) this.map.delete(this.map.keys().next().value);
    this.map.set(key, val);
  }
}

20. 手写函数缓存(记忆化)

function memoize(fn) {
  const cache = {};
  return function (...args) {
    const key = JSON.stringify(args);
    if (cache[key]) return cache[key];
    return cache[key] = fn(...args);
  };
}