2026-01-19 09:34:13 +08:00

4.5 KiB

JS 基础与手写实现

1. 手写 Promise

class MyPromise {
  constructor(executor) {
    this.state = 'pending';
    this.value = undefined;
    this.callbacks = [];
    
    const resolve = (value) => {
      if (this.state !== 'pending') return;
      this.state = 'fulfilled';
      this.value = value;
      this.callbacks.forEach(cb => cb.onFulfilled(value));
    };
    
    const reject = (reason) => {
      if (this.state !== 'pending') return;
      this.state = 'rejected';
      this.value = reason;
      this.callbacks.forEach(cb => cb.onRejected(reason));
    };
    
    try { executor(resolve, reject); } catch (e) { reject(e); }
  }
  
  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      const handle = (callback, fallback) => {
        try {
          const result = (callback || fallback)(this.value);
          result instanceof MyPromise ? result.then(resolve, reject) : resolve(result);
        } catch (e) { reject(e); }
      };
      
      if (this.state === 'fulfilled') handle(onFulfilled, v => v);
      else if (this.state === 'rejected') handle(onRejected, e => { throw e; });
      else this.callbacks.push({
        onFulfilled: () => handle(onFulfilled, v => v),
        onRejected: () => handle(onRejected, e => { throw e; })
      });
    });
  }
}

2. Promise.all / Promise.race

// Promise.all - 所有成功才成功
Promise.myAll = (promises) => {
  return new Promise((resolve, reject) => {
    const results = [];
    let count = 0;
    promises.forEach((p, i) => {
      Promise.resolve(p).then(val => {
        results[i] = val;
        if (++count === promises.length) resolve(results);
      }, reject);
    });
  });
};

// Promise.race - 第一个完成就返回
Promise.myRace = (promises) => {
  return new Promise((resolve, reject) => {
    promises.forEach(p => Promise.resolve(p).then(resolve, reject));
  });
};

3. 防抖与节流

// 防抖:停止触发后执行
function debounce(fn, delay) {
  let timer = null;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

// 节流:固定间隔执行
function throttle(fn, delay) {
  let last = 0;
  return function(...args) {
    const now = Date.now();
    if (now - last >= delay) {
      last = now;
      fn.apply(this, args);
    }
  };
}

4. 深拷贝(处理循环引用)

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

5. 函数柯里化

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

// 使用示例
const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);
curriedAdd(1)(2)(3); // 6
curriedAdd(1, 2)(3); // 6

6. call / apply / bind 实现

// call
Function.prototype.myCall = function(ctx, ...args) {
  ctx = ctx || window;
  const key = Symbol();
  ctx[key] = this;
  const result = ctx[key](...args);
  delete ctx[key];
  return result;
};

// apply
Function.prototype.myApply = function(ctx, args = []) {
  ctx = ctx || window;
  const key = Symbol();
  ctx[key] = this;
  const result = ctx[key](...args);
  delete ctx[key];
  return result;
};

// bind
Function.prototype.myBind = function(ctx, ...args) {
  const fn = this;
  return function(...newArgs) {
    return fn.apply(ctx, [...args, ...newArgs]);
  };
};

7. 事件总线 EventEmitter

class EventEmitter {
  constructor() {
    this.events = {};
  }
  
  on(event, listener) {
    (this.events[event] ||= []).push(listener);
    return this;
  }
  
  off(event, listener) {
    if (this.events[event]) {
      this.events[event] = this.events[event].filter(l => l !== listener);
    }
    return this;
  }
  
  emit(event, ...args) {
    (this.events[event] || []).forEach(listener => listener(...args));
    return this;
  }
  
  once(event, listener) {
    const wrapper = (...args) => {
      listener(...args);
      this.off(event, wrapper);
    };
    return this.on(event, wrapper);
  }
}