Skip to content

1~10 【顶级高频 · 闭眼秒写】

防抖 debounce | 生/熟/秒:

延迟执行,重复触发就重置定时器。三步:timer → clearTimeout → setTimeout。

js
function debounce(fn, delay) {
  let timer; // 闭包保存定时器ID
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args); // 箭头函数继承外层this
    }, delay);
  };
}

节流 throttle | 生/熟/秒:

间隔内只执行一次。记录上次时间戳,差值 >= 间隔才执行。

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

深拷贝 deepClone | 生/熟/秒:

递归复制,WeakMap 处理循环引用。三步:判断基本类型 → 创建容器 → 递归赋值。

js
function deepClone(obj, map = new WeakMap()) {
  if (obj === null || typeof obj !== "object") return obj;
  if (map.has(obj)) return map.get(obj); // 处理循环引用
  let clone = Array.isArray(obj) ? [] : {};
  map.set(obj, clone);
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      clone[key] = deepClone(obj[key], map);
    }
  }
  return clone;
}

Promise 完整实现 | 生/熟/秒:

三状态 + callbacks 队列 + then 返回新 Promise 实现链式调用。

js
class MyPromise {
  constructor(executor) {
    // 状态:pending / fulfilled / rejected
    this.status = "pending";
    // 成功结果 / 失败原因
    this.result = undefined;
    // 成功回调队列
    this.onFulfilledcbs = [];
    // 失败回调队列
    this.onRejectedcbs = [];

    // 成功函数
    const resolve = (value) => {
      if (this.status !== "pending") return;
      this.status = "fulfilled";
      this.result = value;
      // 微任务执行所有成功回调
      queueMicrotask(() => {
        this.onFulfilledcbs.forEach(cb => cb(this.result));
      });
    };

    // 失败函数
    const reject = (reason) => {
      if (this.status !== "pending") return;
      this.status = "rejected";
      this.result = reason;
      // 微任务执行所有失败回调
      queueMicrotask(() => {
        this.onRejectedcbs.forEach(cb => cb(this.result));
      });
    };

    // 执行器异常直接 reject
    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }

  // then 方法
  then(onFulfilled, onRejected) {
    // Promise A+ 规范:不传则使用默认透传函数
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : val => val;
    onRejected = typeof onRejected === "function" ? onRejected : err => { throw err };

    // then 必须返回新 Promise
    return new MyPromise((resolve, reject) => {
      // 封装成功处理逻辑
      const handleSuccess = () => {
        try {
          const res = onFulfilled(this.result);
          resolve(res);
        } catch (err) {
          reject(err);
        }
      };

      // 封装失败处理逻辑
      const handleFail = () => {
        try {
          const res = onRejected(this.result);
          resolve(res);
        } catch (err) {
          reject(err);
        }
      };

      // 状态还在等待 → 推入队列
      if (this.status === "pending") {
        this.onFulfilledcbs.push(handleSuccess);
        this.onRejectedcbs.push(handleFail);
      }
      // 已成功 → 直接执行
      else if (this.status === "fulfilled") {
        queueMicrotask(handleSuccess);
      }
      // 已失败 → 直接执行
      else {
        queueMicrotask(handleFail);
      }
    });
  }
}

数组去重 | 生/熟/秒:

Set 一行搞定;filter + indexOf 是手写思路。

js
const unique = (arr) => [...new Set(arr)];
const unique2 = (arr) => arr.filter((v, i) => arr.indexOf(v) === i);

数组扁平化 flat | 生/熟/秒:

reduce + concat + 递归。

js
function flatten(arr) {
  //递归一般都有边界条件,这里写在这个三元表达式里面了,只要不是数组就可以return了
  return arr.reduce(
    (acc, val) => acc.concat(Array.isArray(val) ? flatten(val) : val),
    [],
  );
}

数组方法实现 map/filter/reduce | 生/熟/秒:

循环 + 回调,注意传入 (item, index, array) 三个参数。

js
Array.prototype.myMap = function (fn) {
  let res = [];
  for (let i = 0; i < this.length; i++) res.push(fn(this[i], i, this));
  return res;
};

Array.prototype.myFilter = function (fn) {
  let res = [];
  for (let i = 0; i < this.length; i++)
    if (fn(this[i], i, this)) res.push(this[i]);
  return res;
};

Array.prototype.myReduce = function (fn, init) {
  let acc = init === undefined ? this[0] : init;
  let start = init === undefined ? 1 : 0;
  for (let i = start; i < this.length; i++) acc = fn(acc, this[i], i, this);
  return acc;
};

call/apply/bind | 生/熟/秒:

核心:区分三者的作用和输入输出内容即可;

  • 弄清楚this是谁,是什么结构
  • 三者区别
js

//fn.apply的作用是让context对象直接临时调用fn,返回结果;谁调用apply谁就是this(普通情况下);args是数组;
Function.prototype.myApply = function (context, args) {
  context = context == null ? window : context;
  const fn = Symbol();
  context[fn] = this;

  // args 必须是数组/类数组,没有就不传
  const result = args ? context[fn](...args) : context[fn]();
  delete context[fn];
  return result;
};

//fn.bind的作用是 永久 返回一个新函数可以让context调用fn; 接受数组?
Function.prototype.myBind = function (context, ...args) {//剩余参数
  const fn = this; //this就是外部fn
  //里面的可以fn理解为一个key
  return function (...newArgs) {
    return fn.apply(context, [...args, ...newArgs]);
  };
};

//call 是立即执行返回结果
Function.prototype.myCall = function (context, ...args) {
  // 上下文处理:null/undefined 指向 window
  context = context == null ? window : context;
  // 用 Symbol 防止属性冲突
  const fn = Symbol();
  context[fn] = this;

  // 执行并拿到结果
  const result = context[fn](...args);
  // 删除临时方法
  delete context[fn];
  return result;
};

new/instanceof | 生/熟/秒:

new:创建对象 → 连原型 → 执行构造函数 → 判断返回值。instanceof:沿原型链找 prototype。

js
function myNew(constructor, ...args) {
  let obj = {};
  obj.__proto__ = constructor.prototype;
  const res = constructor.apply(obj, args);
  return typeof res === "object" && res !== null ? res : obj;
}

function myInstanceof(obj, constructor) {
  let proto = obj.__proto__;
  while (proto) {
    if (proto === constructor.prototype) return true;
    proto = proto.__proto__;
  }
  return false;
}

继承 | 生/熟/秒:

ES5 寄生组合:Parent.call(this) + Object.create(Parent.prototype)。ES6 直接 extends + super。

  • ES5 原型链继承
js
function Parent() {
  this.name = "parent";
}
Parent.prototype.say = function () {
  console.log(this.name);
};

function Child() {
  Parent.call(this);
} // 继承实例属性
Child.prototype = Object.create(Parent.prototype); // 继承原型方法
Child.prototype.constructor = Child;
  • ES6 class 继承
js
class Parent {
  constructor() {
    this.name = "parent";
  }
  say() {
    console.log(this.name);
  }
}
class Child extends Parent {
  constructor() {
    super();
  }
}