学会这79道前端编程题, 一车眉笔都是你的

452 阅读23分钟

前言

大厂前端社招面试的一二面,大致是这么分配时间的,半小时的自我介绍+八股文(一面)/项目(二面),半小时做2-3道题(JS编程题 或者 Leetcode原题),剩下的时间进行反问

所以光一二轮技术面就可能遇到4到6道编程题或者Leetcode题,对于更资深的社招岗位有些公司还会有第三轮技术面,又会考2-3道编程题或者Leetcode题,而往往背景不好的候选人,在做题环节如果有一题没做出来或者紧张或者没做过类似的题想了15分钟最后思路不对,都可能直接进人才库,哪怕前面八股文讲得再好,项目吹得再厉害也救不回来

为此笔者精心整理了79道前端JS编程题,也是笔者在之前准备面试时的一点积累,刷完这些题并把代码和思路烂熟于心,你将能够从容应对大部分前端面试中的JS编程题,愿你在这次学习中收获满满,掌握JS编程题的套路和精髓

Array

1.Array.forEach

Array.prototype.ForEach = function (func) {
  for (let i = 0; i < this.length; i++) {
    func(this[i], i, this);
  }
};

2.Array.filter

Array.prototype.Filter = function (func) {
  const res = [];
  for (let i = 0; i < this.length; i++) {
    func(this[i], i, this) && res.push(this[i]);
  }
  return res;
};

3.Array.reduce

Array.prototype.Reduce = function (func, init) {
  let total = init || this[0];
  const start = init ? 0 : 1;
  for (let i = start; i < this.length; i++) {
    total = func(total, this[i], i, this);
  }
  return total;
};

4.Array.every

Array.prototype.Every = function (func) {
  let flag = true;
  for (let i = 0; i < this.length; i++) {
    if (!func(this[i], i, this)) {
      flag = false;
      break;
    }
  }
  return flag;
};

5.Array.some

Array.prototype.Some = function (func) {
  let flag = false;
  for (let i = 0; i < this.length; i++) {
    if (func(this[i], i, this)) {
      flag = true;
      break;
    }
  }
  return flag;
};

6.Array.map

//map ES6版
Array.prototype.Map = function (func) {
  const res = [];
  for (let i = 0; i < this.length; i++) {
    res.push(func(this[i], i, this));
  }
  return res;
};

//map Reduce版
Array.prototype.Map = function (func) {
  const arr = this;
  return arr.reduce((prev, ele, index, arr) => {
    prev.push(func(ele, index, arr));
    return prev;
  }, []);
};

7.Flatten

function Flatten(arr) {
  let res = [];
  for (let i = 0, len = arr.length; i < len; i++) {
    if (Array.isArray(arr[i])) res = res.concat(Flatten(arr[i]));
    else res.push(arr[i]);
  }
  return res;
}

8.Shuffle

function Shuffle(arr) {
  for (let i = 0; i < arr.length; i++) {
    // Math.floor([0,1)*size) => [0,size-1]
    let index = Math.floor(Math.random() * arr.length);
    [arr[i], arr[index]] = [arr[index], arr[i]];
  }
}

9.Distinct

function Distinct(arr, method) {
  const seen = new Set();
  return arr.filter((obj) => {
    const key = method ? method(obj) : JSON.stringify(obj);
    if (!seen.has(key)) {
      seen.add(key);
      return true;
    }
    return false;
  });
}

10.Chunk


chunk([1,2,3,4,5], 1)
// [[1], [2], [3], [4], [5]]

chunk([1,2,3,4,5], 2)
// [[1, 2], [3, 4], [5]]

chunk([1,2,3,4,5], 3)
// [[1, 2, 3], [4, 5]]

chunk([1,2,3,4,5], 4)
// [[1, 2, 3, 4], [5]]

chunk([1,2,3,4,5], 5)
// [[1, 2, 3, 4, 5]]

function chunk(array, size) {
  const chunkedArray = [];
  for (let i = 0; i < array.length; i += size) {
    chunkedArray.push(array.slice(i, i + size));
  }
  return chunkedArray;
}

Function

11.Function.Apply

Function.prototype.Apply = function (ctx, arr) {
  ctx.func = this;
  let res = ctx.func(...arr);
  delete ctx.func;
  return res;
};

12.Function.Bind

Function.prototype.Bind = function (context, ...boundArgs) {
  const originalFunction = this;

  return function (...args) {
    if (this instanceof originalFunction) {
      // 如果通过 new 调用,则使用 new 关键字创建新实例
      return new originalFunction(...boundArgs.concat(args));
    } else {
      // 否则,使用提供的上下文
      return originalFunction.apply(context, boundArgs.concat(args));
    }
  };
};

13.Function.Call

Function.prototype.Call = function (ctx, ...args) {
  ctx.func = this;
  let res = ctx.func(...args);
  delete ctx.func;
  return res;
};

14.Curry

function Curry(fn, ...args1) {
  return args1.length >= fn.length
    ? fn.call(this, ...args1)
    : function (...args2) {
        return Curry(fn, ...args1, ...args2);
      };
}

function add(a, b, c) {
  return a + b + c;
}

const curriedAdd = Curry(add);

console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6
console.log(curriedAdd(1)(2, 3)); // 6

15.Compose

function Compose(...fns) {
  return function (x) {
    return fns.reduceRight((result, fn) => fn.call(this, result), x);
  };
}

const add5 = (x) => x + 5;
const double = (x) => x * 2;
const subtract3 = (x) => x - 3;

// 组合函数
const composedFunction = Compose(subtract3, double, add5);

const result = composedFunction(10); // 先加5,再乘2,最后减3,结果为 17
console.log(result); // 输出 27

16.Pipe

const times = (y) => (x) => x * y;
const plus = (y) => (x) => x + y;
const subtract = (y) => (x) => x - y;
const divide = (y) => (x) => x / y;

console.log(pipe([times(2), times(3)])(1));
// 1 * 2 * 3 = 6

console.log(pipe([times(2), plus(3), times(4)])(2));
// (2 * 2 + 3) * 4 = 28

console.log(pipe([times(2), subtract(3), divide(4)])(3));
// (3 * 2 - 3) / 4 = 0.75

function pipe(functions) {
  return function (x) {
    return functions.reduce((acc, func) => func.call(this, acc), x);
  };
}

17.Debounce

function debounce(func, wait, options = { leading: false, trailing: true }) {
  let timer = null, lock = false;
  return function (...args) {
    if (!options.leading) {
      clearTimeout(timer);
      timer = setTimeout(() => {
        options.trailing && func.apply(this, args);
      }, wait);
    } else {
      if (!lock) {
        func.apply(this, args);
        lock = true;
      } else {
        clearTimeout(timer);
      }
      timer = setTimeout(() => {
        lock = false;
      }, wait);
    }
  };
}

18.Throttle

function throttle(fn, interval, options = { leading: true, trailing: true }) {
  let lastTimestap = 0,
    timer = null;
  return function (...args) {
    let currentTimestap = new Date();
    clearTimeout(timer);
    // 让lastTimestap初始值变为currentTimestap进入定时器逻辑
    if (!options.leading && !lastTimestap) lastTimestap = currentTimestap;
    // options.leading处理leading和trailing同时为false的情况
    if (options.leading && currentTimestap - lastTimestap >= interval) {
      fn.apply(this, args);
      lastTimestap = currentTimestap;
    } else if (options.trailing) {
      const remaining = interval - (currentTimestap - lastTimestap);
      timer = setTimeout(() => {
        fn.apply(this, args);
        // 定时器结束后重置lastTimestap为0
        lastTimestap = options.leading ? new Date() : 0;
      }, remaining);
    }
  };
}

19.Repeat

function Repeat(func, times, wait) {
  return function (...args) {
    let count = 0;
    let interval = setInterval(function () {
      count += 1;
      func.call(this, ...args);
      if (count === times) {
        clearInterval(interval);
      }
    }, wait);
  };
}

20.Memo

const func = (arg1, arg2) => {
  return arg1 + arg2
}

const memoed1 = memo(func)

memoed1(1, 2) 
// 3, func 被调用

memoed1(1, 2) 
// 3,func 未被调用 

memoed1(1, 3)
// 4,新参数,func 被调用

const memoed2 = memo(func, () => 'samekey')

memoed2(1, 2) 
// 3,func被调用,缓存key是 'samekey'

memoed2(1, 2) 
// 3,因为key是一样的,3被直接返回,func未调用

memoed2(1, 3) 
// 3,因为key是一样的,3被直接返回,func未调用

function memo(func, resolver = (...args) => args.join("_")) {
  const cache = new Map();

  return function (...args) {
    const cacheKey = resolver(...args);
    if (cache.has(cacheKey)) {
      return cache.get(cacheKey);
    }
    const value = func.apply(this, args);
    cache.set(cacheKey, value);
    return value;
  };
}

21.add

function add(x) {
  // 定义一个内部函数用于处理累积
  const innerAdd = (y) => {
    // 返回一个新的包装函数,将参数相加
    return add(x + y);
  };

  // 定义一个valueOf方法,以支持直接输出计算结果
  innerAdd.valueOf = () => x;

  return innerAdd;
}

console.log(add(1)(2)(3)); // 输出: 6
console.log(add(1)(2)(3)(4)); // 输出: 10
console.log(add(1)(2)(3)(4)(5)); // 输出: 15

22.sum

function sum(a) {
  let currentSum = a;

  function innerSum(b) {
    currentSum += b;
    return innerSum;
  }

  innerSum.valueOf = function () {
    return currentSum;
  };

  return innerSum;
}

const sum1 = sum(1);
console.log(sum1(2) == 3); // 输出: true
console.log(sum1(3) == 4); // 输出: true

console.log(sum(1)(2)(3) == 6); // 输出: true
console.log(sum(5)(-1)(2) == 6); // 输出: true

Object

23.New

function New(constructor, ...args) {
  //以构造器的 prototype 属性(注意与私有字段[[prototype]]的区分)为原型,创建新对象
  const obj = Object.create(constructor.prototype);
  //将 this 和调用参数传给构造器,执行
  const result = constructor.apply(obj, args);
  //如果构造器返回的是对象,则返回,否则返回第一步创建的对象
  return typeof result === "object" && result !== null ? result : obj;
}

24.InstanceOf

function InstanceOf(inst, type) {
  if (typeof inst !== "object") return false;
  while (inst) {
    if (type.prototype === inst.__proto__) return true;
    inst = inst.__proto__;
  }
  return false
}

25.Create

function Create(target, propertiesObject) {
  // const obj = {}
  // obj.__proto__ = target

  function F() {}
  F.prototype = target;
  const obj = new F();

  if (propertiesObject !== undefined) {
    Object.defineProperties(obj, propertiesObject);
  }

  return obj;
}

26.Inherit

function Inherit(subType, superType) {
  subType.prototype = Object.create(superType.prototype);
  subType.prototype.constructor = subType;
}

27.Extends

const InheritedSubType = Extends(SuperType, SubType)
const instance = new InheritedSubType()
// 等价于
class SubType extends SuperType {}
const instance = new SubType()

function Extends(SuperType, SubType) {
  function ExtendedType(...args) {
    SuperType.apply(this, args);
    SubType.apply(this, args);

    Object.setPrototypeOf(this, SubType.prototype)
  }

  Object.setPrototypeOf(SubType.prototype, SuperType.prototype);
  Object.setPrototypeOf(ExtendedType.prototype, SubType.prototype);
  Object.setPrototypeOf(ExtendedType, SuperType);
  
  return ExtendedType;
}

28.DeepClone

function deepClone(obj, clones = new WeakMap()) {
  // 如果是基本数据类型,则直接返回
  if (obj === null || typeof obj !== "object") {
    return obj;
  }

  // 如果已经拷贝过该对象,则返回拷贝后的对象,防止循环引用
  if (clones.has(obj)) {
    return clones.get(obj);
  }

  // 处理 Set 类型
  if (obj instanceof Set) {
    const setClone = new Set();
    clones.set(obj, setClone);
    obj.forEach((item) => {
      setClone.add(deepClone(item, clones));
    });
    return setClone;
  }

  // 处理 Map 类型
  if (obj instanceof Map) {
    const mapClone = new Map();
    clones.set(obj, mapClone);
    obj.forEach((value, key) => {
      mapClone.set(key, deepClone(value, clones));
    });
    return mapClone;
  }

  // 处理引用数据类型,继承原型上的属性
  const clonedObj = Object.create(Object.getPrototypeOf(obj));
  clones.set(obj, clonedObj);

  // 递归拷贝属性
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      clonedObj[key] = deepClone(obj[key], clones);
    }
  }

  return clonedObj;
}

function deepClone(obj, map = new WeakMap()) {
  if (typeof obj !== "object" || obj === null) 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;
}

const obj = { a: 1, b: [2] };
obj.c = obj;
const cloned = deepClone(obj);

console.log(cloned !== obj); // true
console.log(cloned.c === cloned); // true
console.log(cloned.b !== obj.b); // true

29.a==1 && a==2 && a==3

let value = 1;

const a = {
  valueOf: function () {
    return value++;
  },
  toString: function () {
    return value++;
  },
};

console.log(a == 1 && a == 2 && a == 3); // true

30.a===1 && a===2 && a===3

let value = 1;

Object.defineProperty(global, "a", {
  get: function () {
    return value++;
  },
});

console.log(a === 1 && a === 2 && a === 3); // true

Promise

31.Promise.prototype.catch

Promise.prototype.Catch = function (onRejected) {
  return this.then(undefined, onRejected);
};

32.Promise.prototype.finally

Promise.prototype.Finally = function (onFinally) {
  return this.then(
    (val) => {
      onFinally();
      return val;
    },
    (err) => {
      onFinally();
      throw err;
    }
  );
};

33.Promise.resolve

function promiseResolve(value) {
  if (value instanceof Promise) return value;
  return new Promise((resolve, reject) => {
    if (isPromiseLike(value)) {
      value.then(resolve, reject);
    } else {
      resolve(value);
    }
  });
}

const isPromiseLike = (promise) =>
  promise !== null &&
  typeof promise === "object" &&
  typeof promise.then === "function";

34.Promise.reject

function promiseReject(reason) {
  return new Promise((resolve, reject) => {
    reject(reason);
  });
}

35.Promise.all

// all: 全部成功算成功,一个失败或异常算失败
function promiseAll(promises) {
  return new Promise((resolve, reject) => {
    let count = 0;
    let res = []
    for (const entry of promises.entries()) {
      const [index, promise] = entry
      
      Promise.resolve(promise)
        .then(value => {
          res[index] = value;
          count++;
          if (count === promises.length) {
            resolve(res)
          }
        })
        .catch(reason => reject(reason));
    }
  });
}

36.Promise.allSettled

// allSettled: 不管成功失败,一起返回成功
function promiseAllSettled(promises) {
  const result = [];
  for (let p of promises) {
    result.push(
      Promise.resolve(p).then(
        (value) => ({ value, status: "FULFILLED" }),
        (reason) => ({ reason, status: "REJECTED" })
      )
    );
  }
  return Promise.all(result);
}

37.Promise.race

// race: 第一个成功就成功,第一个失败就失败
function promiseRace(promises) {
  return new Promise((resolve, reject) => {
    for (let p of promises) {
      Promise.resolve(p).then(resolve, reject);
    }
  });
}

38.Promise.any

// any: 一个成功就成功,全部失败才失败
function promiseAny(promises) {
  return new Promise((resolve, reject) => {
    let rejectedCount = 0;
    let count = 0;
    for (const promise of promises) {
      count++;
      Promise.resolve(promise)
        .then(resolve)
        .catch(() => {
          rejectedCount++;
          if (rejectedCount === count) {
            reject(new AggregateError("All promises were rejected"));
          }
        });
    }
  });
}

39.Promise.last

// last: 全部成功返回最后一个成功的,一个失败算失败
function promiseLast(promises) {
  return new Promise((resolve, reject) => {
    let count = 0;
    for (const promise of promises) {
      Promise.resolve(promise)
        .then((value) => {
          count++;
          if (count === promises.length) {
            resolve(value);
          }
        })
        .catch((reason) => reject(reason));
    }
  });
}

40.Promise.promisify

function promisify(asyncFunction) {
  return function (...args) {
    return new Promise((resolve, reject) => {
      asyncFunction(...args, (err, result) => {
        if (err) {
          reject(err);
        } else {
          resolve(result);
        }
      });
    });
  };
}

const fs = require("fs");

const readFileAsync = promisify(fs.readFile);

readFileAsync("example.txt", "utf8")
  .then((data) => {
    console.log(data);
  })
  .catch((err) => {
    console.error(err);
  });

41.Promise.retry

function promiseRetry(fn, maxRetries, delay) {
  return new Promise((resolve, reject) => {
    function attempt() {
      fn()
        .then(resolve)
        .catch((error) => {
          if (maxRetries === 0) {
            reject(error); // 达到最大重试次数后仍然失败,直接拒绝 Promise
          } else {
            maxRetries--;
            setTimeout(attempt, delay); // 延迟后进行重试
          }
        });
    }

    attempt();
  });
}

// 示例用法
function simulateAsyncTask() {
  return new Promise((resolve, reject) => {
    const random = Math.random();
    if (random < 0.8) {
      resolve("Success");
    } else {
      reject("Failed");
    }
  });
}

promiseRetry(simulateAsyncTask, 3, 1000)
  .then((result) => {
    console.log("Task completed successfully:", result);
  })
  .catch((error) => {
    console.error("Task failed after retries:", error);
  });

42.pLimit

// pLimit是一个函数,执行fn,并发量是2,返回Promise
const pLimit = (concurrency) => {
  const queue = [];
  let activeCount = 0;

  const next = () => {
    activeCount--;

    if (queue.length > 0) {
      queue.shift()();
    }
  };

  const run = async (fn, resolve, ...args) => {
    activeCount++;

    const result = (async () => fn(...args))();

    resolve(result);

    try {
      await result;
    } catch {}

    next();
  };

  const enqueue = (fn, resolve, ...args) => {
    queue.push(run.bind(null, fn, resolve, ...args));

    if (activeCount < concurrency && queue.length > 0) {
      queue.shift()();
    }
  };

  const generator = (fn, ...args) =>
    new Promise((resolve) => {
      enqueue(fn, resolve, ...args);
    });

  return generator;
};

const limit = pLimit(2);

function asyncFun(value, delay) {
  return new Promise((resolve) => {
    console.log("start " + value);
    setTimeout(() => resolve(value), delay);
  });
}

(async function () {
  const arr = [
    limit(() => asyncFun("aaa", 2000)),
    limit(() => asyncFun("bbb", 3000)),
    limit(() => asyncFun("ccc", 1000)),
    limit(() => asyncFun("ccc", 1000)),
    limit(() => asyncFun("ccc", 1000)),
  ];

  const result = await Promise.all(arr);
  console.log(result);
})();

43.sequence

function promisify(func, input) {
  return new Promise((resolve, reject) => {
    func((err, data) => {
      if (!err) resolve(data);
      reject(err);
    }, input);
  });
}

function sequence(funcs) {
  return async function (callback, initData) {
    let ret = initData;
    try {
      for (let func of funcs) {
        ret = await promisify(func, ret);
      }
    } catch (ex) {
      callback(ex, ret);
    }
    callback(undefined, ret);
  };
}

const asyncTimes2 = (callback, num) => {
  setTimeout(() => callback(null, num * 2), 100);
};
const asyncTimes4 = sequence([asyncTimes2, asyncTimes2]);

asyncTimes4((error, data) => {
  console.log(data); // 4
}, 1);

44.schedule

function schedule(n) {
  // 返回一个函数,该函数接受一个任务数组并按顺序运行它们
  return function (tasks) {
    const res = []; // 存储任务的结果
    let queue = []; // 任务队列
    let cur = 0; // 当前正在运行的任务数
    let finished = 0; // 已完成的任务数

    // 将任务队列初始化为带有索引的任务对象
    queue = tasks.map((v, index) => ({
      task: v,
      index,
    }));

    return new Promise((resolve) => {
      function runTask() {
        // 当仍有任务待运行且未达到并发数上限时
        while (cur < n && queue[0]) {
          const item = queue.shift(); // 取出队列中的任务项

          item.task().then((result) => {
            res[item.index] = result; // 存储任务结果
            cur--; // 当前运行任务数减一
            finished++; // 已完成任务数加一

            if (finished === tasks.length) {
              // 所有任务已完成,触发 Promise 的解析
              resolve(res);
            } else {
              // 继续运行任务
              runTask(resolve);
            }
          });

          cur++; // 当前运行任务数加一
        }
      }

      runTask(); // 开始运行任务
    });
  };
}

const runTask = schedule(4); // 创建一个并发数为4的任务调度器

const tasks = new Array(10).fill(0).map(
  (x, i) => () =>
    new Promise((resolve) => {
      setTimeout(() => {
        console.log(`task${i} complete`);
        resolve(`task${i}`);
      }, 2000);
    })
);

runTask(tasks).then(console.log);

// 预期输出
// 2s 后
// task0 complete
// task1 complete
// task2 complete
// task3 complete
// 再2s 后
// task4 complete
// task5 complete
// task6 complete
// task7 complete
// 再2s 后
// task8 complete
// task9 complete
// ['task0', 'task1', ..., 'task9']

45.AsyncQueue

class AsyncQueue {
  constructor() {
    this.events = {};
  }

  // 添加事件处理函数到指定事件名的队列中
  tap(name, fn) {
    if (this.events[name]) {
      this.events[name].push(fn); // 如果已存在该事件队列,将函数添加到队列末尾
    } else {
      this.events[name] = [fn]; // 否则创建新的事件队列
    }
  }

  // 执行指定事件名的队列中的所有事件处理函数
  exec(name, fn) {
    if (this.events[name]) {
      const dispatch = (i) => {
        const event = this.events[name][i];
        if (event) {
          // 执行事件处理函数,并传入一个回调函数,以便在事件完成后继续执行下一个事件
          event(() => dispatch(i + 1));
        } else {
          fn(); // 所有事件执行完毕后,执行传入的回调函数
        }
      };

      dispatch(0); // 从队列中的第一个事件开始执行
    }
  }
}

function fn1(cb) {
  console.log("fn1");
  cb();
}

function fn2(cb) {
  console.log("fn2");
  cb();
}

function fn3(cb) {
  setTimeout(() => {
    console.log("fn3");
    cb();
  }, 2000);
}

function fn4(cb) {
  setTimeout(() => {
    console.log("fn4");
    cb();
  }, 3000);
}

const asyncQueue = new AsyncQueue(); // 创建一个异步队列实例

// 添加事件处理函数到名为 'init' 的事件队列中
asyncQueue.tap("init", fn1);
asyncQueue.tap("init", fn2);
asyncQueue.tap("init", fn3);
asyncQueue.tap("init", fn4);

// 执行名为 'init' 的事件队列中的所有事件处理函数,并在完成后执行回调函数
asyncQueue.exec("init", () => {
  console.log("执行结束");
});

// 预期输出
// fn1
// fn2
// 2s 后
// fn3
// 再 3s 后
// fn4
// 执行结束

46.请求错误重试

function request(url, maxCount = 5) {
  return fetch(url).catch((err) => {
    maxCount <= 0 ? Promise.reject(err) : request(url, maxCount - 1);
  });
}

47.请求超时重试

/**
 * @param {一次请求的最大响应时间} time 
 * @param {最大超时请求次数} limit 
 * @param {资源加载函数} fn 
 * @returns Promise
 */
function request(time, limit, fn) {
  let retryCount = 0;

  async function tryRequest() {
    try {
      return await Promise.race([fn(), timeoutFail(time)]);
    } catch (error) {
      retryCount++;
      if (retryCount <= limit) {
        console.log(`Retry #${retryCount}`);
        return tryRequest();
      } else {
        throw new Error("Max retry limit reached");
      }
    }
  }

  return tryRequest();
}

function timeoutFail(time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject(new Error("Request timed out"));
    }, time);
  });
}
function sleep(time = 2000, val = 1) {
  return new Promise((resolve) =>
    setTimeout(() => {
      resolve(val);
    }, time)
  );
}

// 调用 sleep 函数,最多重试 3 次,每次超时等待 1000 毫秒
request(1000, 3, sleep)
  .then((data) => {
    console.log(data);
  })
  .catch((error) => {
    console.error(error.message);
  });

48.并发任务控制(声明式)

const arr = [];
const startTime = new Date();

const sleep = (i, val) => {
  return () =>
    new Promise((resolve) => {
      console.log(`${i}进入队列`);
      setTimeout(() => {
        resolve(val);
      }, 1000 * i);
    }).then((val) => {
      const endTime = new Date();
      console.log(`${endTime - startTime}ms后 ${i}完成`);
      return val;
    });
};

arr.push(
  () => sleep(1, 10),
  () => sleep(1, 10),
  () => sleep(5, 5),
  () => sleep(3, 3),
  () => sleep(4, 4),
  () => sleep(5, 5),
  () => sleep(3, 3)
);

async function concurrentRequest(tasks, n) {
  const res = [];
  const set = new Set();
  for (let entry of tasks.entries()) {
    const [index, fn] = entry;
    const p = fn()().then((val) => {
      res.push(val); // 按照任务完成的顺序排列
      // res[index] = val // 按照任务执行的顺序排列
      set.delete(p);
    });
    set.add(p);
    if (set.size === n) {
      await Promise.race(set);
    }
  }
  await Promise.allSettled(set);
  return res;
}

function concurrentRequest2(tasks, n) {
  return new Promise((resolve) => {
    let count = 0;
    let index = 0;
    let result = [];

    async function request() {
      if (index === tasks.length) {
        return;
      }
      const i = index;
      const task = tasks[index++];
      try {
        const res = await task()();
        result[i] = res;
      } catch (e) {
        result[i] = e;
      } finally {
        count++;
        if (count === tasks.length) {
          resolve(result);
        }
        request();
      }
    }

    const times = Math.min(tasks.length, n);
    for (let i = 0; i < times; i++) {
      request();
    }
  });
}

concurrentRequest2(arr, 2).then((res) => {
  console.log(res);
});
//   1进入队列
//   1进入队列
//   1006ms后 1完成
//   5进入队列
//   1006ms后 1完成
//   3进入队列
//   4007ms后 3完成
//   4进入队列
//   6007ms后 5完成
//   5进入队列
//   8008ms后 4完成
//   3进入队列
//   11009ms后 5完成
//   11009ms后 3完成
//   [
//     10, 10, 5, 3,
//      4,  5, 3
//   ]

const delay = (ms, val) => () =>
  new Promise((resolve) => {
    setTimeout(resolve, ms, val);
  });
const delayError = (ms, val) => () =>
  new Promise((resolve, reject) => {
    setTimeout(reject, ms, val);
  });

const promisePool = (functions, limit) => {
  let i = 0;
  const results = [];
  let active = 0;
  return new Promise((resolve) => {
    function next() {
      if (i === functions.length && active === 0) {
        resolve(results);
        return;
      }
      while (active < limit && i < functions.length) {
        const cur = i;
        active++;
        functions[i++]()
          .then((res) => {
            results[cur] = res;
            console.log("fulfilled: " + res);
          })
          .catch((err) => {
            results[cur] = err;
            console.log("rejected: " + err);
          })
          .finally(() => {
            active--;
            next();
          });
      }
    }
    next();
  });
};

promisePool(
  [
    delay(1000, 1),
    delay(2000, 2),
    delay(20, 3),
    delayError(60, 4),
    delay(50, 5),
  ],
  2
).then(console.log)
// fulfilled: 1
// fulfilled: 3
// rejected: 4
// fulfilled: 5
// fulfilled: 2
// [ 1, 2, 3, 4, 5 ]

49.并发任务控制(命令式)

function timeout(time) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, time);
  });
}

class SuperTask {
  constructor(limit = 2) {
    this.limit = limit;
    this.queue = [];
    this.running = 0;
  }
  add(task) {
    return new Promise((resolve, reject) => {
      this.queue.push({ task, resolve, reject });
      // 取出任务并执行的情况
      // 1. add => this.running < this.limit => run()
      // 2. task().then(run)

      if (this.running < this.limit) {
        this.run();
      }
    });
  }

  run() {
    const { task, resolve, reject } = this.queue.shift();
    this.running++;
    task()
      .then(resolve, reject)
      .finally(() => {
        this.running--;
        if (this.queue.length) {
          this.run();
        }
      });
  }
}

const superTask = new SuperTask(2);
const lastTimestap = new Date();
function addTask(time, name) {
  superTask
    .add(() => timeout(time))
    .then(() =>
      console.log(`任务${name}完成, time spent:${new Date() - lastTimestap}`)
    );
}

addTask(10000, 1); // 任务1完成, time spent:10000
addTask(5000, 2); // 任务2完成, time spent:5003
addTask(3000, 3); // 任务3完成, time spent:8007
addTask(4000, 4); // 任务4完成, time spent:12008
addTask(5000, 5); // 任务5完成, time spent:15002

class LimitPromise {
  constructor(max) {
    this._max = max;
    this._count = 0;
    this._taskQueue = [];
    this._instance = null;
  }
  run(caller) {
    return new Promise((resolve, reject) => {
      const task = this._createTask(caller, resolve, reject);
      if (this._count >= this._max) {
        this._taskQueue.push(task);
      } else {
        task();
      }
    });
  }
  _createTask(caller, resolve, reject) {
    return () => {
      caller()
        .then(resolve)
        .catch(reject)
        .finally(() => {
          this._count--;
          if (this._taskQueue.length > 0) {
            const task = this._taskQueue.shift();
            task();
          }
        });
      this._count++;
    };
  }
  static getInstance(max) {
    if (!this._instance) {
      this._instance = new LimitPromise(max);
    }
    return this._instance;
  }
}

const pool = LimitPromise.getInstance(2);
const lastTimeStamp = new Date();

const request = (index, ms) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(index);
    }, ms);
  });
};

const times = [1000, 10000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000];
for (let i = 0; i < 10; i++) {
  pool
    .run(() => request(i, times[i]))
    .then((i) =>
      console.log(`task${i} done, time spent:`, new Date() - lastTimeStamp)
    );
}
// task0 done, time spent: 1001
// task2 done, time spent: 2006
// task3 done, time spent: 3008
// task4 done, time spent: 4010
// task5 done, time spent: 5012
// task6 done, time spent: 6014
// task7 done, time spent: 7017
// task8 done, time spent: 8018
// task9 done, time spent: 9019
// task1 done, time spent: 10002

50.红绿灯任务控制

function red() {
  console.log("red");
}
function green() {
  console.log("green");
}
function yellow() {
  console.log("yellow");
}

// callback version
// const task = (timer, lightFn, callback) => {
//   setTimeout(() => {
//     lightFn()
//     callback();
//   }, timer);
// };

// const step = () => {
//   task(3000, red, () => {
//     task(1000, green, () => {
//       task(2000, yellow, step);
//     });
//   });
// };

// step();

// promise version
// const task = (timer, cb) =>
//   new Promise((resolve, reject) => {
//     setTimeout(() => {
//       cb();
//       resolve();
//     }, timer);
//   });

// const step = () => {
//   task(3000, red)
//     .then(() => task(1000, green))
//     .then(() => task(2000, yellow))
//     .then(step);
// };

// step();

// async/await version
const task = (timer, light) =>
  new Promise((resolve, reject) => {
    setTimeout(() => {
      light();
      resolve();
    }, timer);
  });

const taskRunner = async () => {
  await task(3000, red);
  await task(1000, green);
  await task(2000, yellow);
  taskRunner();
};

taskRunner();

Others

51.LRU

class LRUCache {
  constructor(capacity) {
    this.capacity = capacity;
    this.cache = new Map();
  }

  get(key) {
    if (!this.cache.has(key)) return -1;
    const val = this.cache.get(key);
    this.cache.delete(key);
    this.cache.set(key, val);
    return val;
  }

  put(key, value) {
    if (this.cache.has(key)) {
      this.cache.delete(key);
    }
    if (this.cache.size === this.capacity) {
      const oldestKey = this.cache.keys().next().value;
      this.cache.delete(oldestKey);
    }
    this.cache.set(key, value);
  }
}

const obj = new LRUCache(2);
obj.put(1, 1);
obj.put(2, 2);
console.log(obj.get(1)); // 输出 1
obj.put(3, 3); // 删除键 2
console.log(obj.get(2)); // 输出 -1,因为键 2 不再存在于缓存中

52.洋葱模型

class TaskPool {
  constructor() {
    this.tasks = [];
    this.currentIndex = 0;
    this.running = false;

    this.next = async () => {
      this.currentIndex++;
      await this.runTask();
    };
  }

  addTask(task) {
    this.tasks.push(task);
  }

  run() {
    if (this.running || !this.tasks.length) return;
    this.running = true;
    //取出一个任务执行
    this.runTask();
  }

  async runTask() {
    const i = this.currentIndex;
    if (i >= this.tasks.length) {
      this.running = false;
      return;
    }

    const fn = this.tasks[this.currentIndex];
    await fn(this.next);

    const j = this.currentIndex;
    if (i === j) {
      await this.next();
    }
  }
}

const sleep = (t) => new Promise((resolve) => setTimeout(resolve, t * 1000));

const t = new TaskPool();

t.addTask(async (next) => {
  console.log(1, "start");
  await next();
  console.log(1, "end");
});
t.addTask(async () => {
  console.log(2);
  await sleep(2);
  console.log('after 2s')
});
t.addTask(() => {
  console.log(3);
});
t.run(); // 1 start, 2, after 2s, 3, 1 end

53.EventEmitter

class EventEmitter {
  constructor() {
    this.events = {};
  }

  on(event, listener) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(listener);
  }

  emit(event, ...args) {
    if (this.events[event]) {
      this.events[event].forEach((listener) => {
        listener(...args);
      });
    }
  }

  off(event, listener) {
    if (this.events[event]) {
      this.events[event] = this.events[event].filter((l) => l !== listener);
    }
  }

  once(event, listener) {
    const onceWrapper = (...args) => {
      listener(...args);
      this.off(event, onceWrapper);
    };
    this.on(event, onceWrapper);
  }
}

54.串行执行器

const runHooks = (hooks, params, cb) => {
  let index = 0;

  function next(result) {
    if (result === false || index === hooks.length - 1) {
      cb(result);
    } else {
      hooks[index++](params, next);
    }
  }

  next();
};

const hook1 = (params, next) => {
  console.log("1");
  setTimeout(() => {
    console.log("1-1");
    next();
    console.log("1-2");
  }, 1000);
};

const hook2 = (params, next) => {
  console.log("2");
  next(false);
};

const hook3 = (params, next) => {
  console.log("3");
  next(false);
};

runHooks([hook1, hook2, hook3], {}, (result) => {
  console.log("done", result);
});

// 1
// 1-1 // 1s之后
// 2
// done, false
// 1-2

55.LazyMan

class LazyManGenerator {
  constructor(name) {
    this.taskArray = [];
    // 初始化任务
    const task = () => {
      console.log(`Hi! This is ${name}`); //
      // 执行完初始化任务后,继续执行下一个任务
      this.next();
    };

    // 将初始化任务放入任务队列中
    this.taskArray.push(task);

    setTimeout(() => {
      this.next();
    }, 0);
  }
  next() {
    // 取出下一个任务并执行
    const task = this.taskArray.shift();
    task && task();
  }
  sleep(time) {
    this.sleepTask(time, false);
    // return this 保持链式调用
    return this;
  }
  sleepFirst(time) {
    this.sleepTask(time, true);
    return this;
  }
  sleepTask(time, prior) {
    const task = () => {
      setTimeout(() => {
        console.log(`Wake up after ${time}`);
        this.next();
      }, time * 1000);
    };
    if (prior) {
      this.taskArray.unshift(task);
    } else {
      this.taskArray.push(task);
    }
  }
  eat(name) {
    const task = () => {
      console.log(`Eat ${name}`);
      this.next();
    };
    this.taskArray.push(task);
    return this;
  }
}
function LazyMan(name) {
  return new LazyManGenerator(name);
}

LazyMan("Hank");
// Hi! This is Hank!
LazyMan("Hank").sleep(10).eat("dinner"); // Hi! This is Hank!
// 10 ..
// Wake up after 10
// Eat dinner~
LazyMan("Hank").eat("dinner").eat("supper"); // Hi This is Hank!
// Eat dinner~
// Eat supper~
LazyMan("Hank").sleepFirst(5).eat("supper"); // 5
// Wake up after 5
// Hi This is Hank!
// Eat supper

function arrange(name) {
  const tasks = [];
  tasks.push(() => {
    console.log(`Hi! This is ${name}!`);
  });
  const lastTimestamp = Date.now();
  return {
    wait: function (time) {
      tasks.push(
        () =>
          new Promise((resolve) =>
            setTimeout(() => {
              resolve();
              console.log(`Wake up after ${new Date() - lastTimestamp}ms`);
            }, time * 1000)
          )
      );
      return this;
    },
    do: function (name) {
      tasks.push(() => {
        console.log(`Eat ${name}`);
      });
      return this;
    },
    waitFirst: function (time) {
      tasks.unshift(
        () =>
          new Promise((resolve) =>
            setTimeout(() => {
              resolve();
              console.log(`Wake up after ${new Date() - lastTimestamp}ms`);
            }, time * 1000)
          )
      );
      return this;
    },
    execute: function () {
      (async () => {
        for (const task of tasks) {
          await task();
        }
      })();
      return this;
    },
  };
}

arrange("Hank").wait(5).do("dinner").waitFirst(3).execute();
// Wake up after 3002ms
// Hi! This is Hank!
// Wake up after 8009ms
// Eat dinner

56.URLSearchParams

function parseURLParam(url) {
  const params = {};
  // 使用URL API来获取查询字符串
  const queryString = new URL(url).search.slice(1);
  // 将查询字符串分割成单独的键值对
  const pairs = queryString.split("&");

  for (const pair of pairs) {
    // 分割键和值
    let [key, value] = pair.split("=");

    // 如果没有指定值,设置为true
    if (value === undefined) {
      params[key] = true;
    } else {
      // 解码值
      value = decodeURIComponent(value);
      // 尝试将值转换为数字
      const convertedValue = isNaN(value) ? value : Number(value);

      // 检查params中是否已经存在该key
      if (key in params) {
        // 如果已存在并且不是数组,则转换为数组
        if (!Array.isArray(params[key])) {
          params[key] = [params[key]];
        }
        // 将新值添加到数组中
        params[key].push(convertedValue);
      } else {
        // 如果不存在,则直接添加键值对
        params[key] = convertedValue;
      }
    }
  }

  return params;
}

let url = "http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled";
console.log(parseURLParam(url)); // { user: 'anonymous', id: [ 123, 456 ], city: '北京', enabled: true }

class URLSearchParams {
  /**
   * @param {string} init
   */
  constructor(init) {
    this.params = new Map();
    if (init) {
      this.parseInit(init);
    }
  }

  parseInit(init) {
    const paramString = init.startsWith("?") ? init.slice(1) : init;
    const pairs = paramString.split("&");
    for (const pair of pairs) {
      const [name, value] = pair.split("=");
      this.append(decodeURIComponent(name), decodeURIComponent(value));
    }
  }

  /**
   * @param {string} name
   * @param {any} value
   */
  append(name, value) {
    if (!this.params.has(name)) {
      this.params.set(name, []);
    }
    this.params.get(name).push(value);
  }

  /**
   * @param {string} name
   */
  delete(name) {
    this.params.delete(name);
  }

  /**
   * @returns {Iterator}
   */
  entries() {
    return this.params.entries();
  }

  /**
   * @param {(value, key) => void} callback
   */
  forEach(callback) {
    for (const [key, value] of this.params) {
      value.forEach((val) => callback(val, key));
    }
  }

  /**
   * @param {string} name
   * @return {string | null}
   */
  get(name) {
    return this.params.has(name) ? this.params.get(name)[0] : null;
  }

  /**
   * @param {string} name
   * @return {string[]}
   */
  getAll(name) {
    return this.params.has(name) ? this.params.get(name) : [];
  }

  /**
   * @param {string} name
   * @return {boolean}
   */
  has(name) {
    return this.params.has(name);
  }

  /**
   * @return {Iterator}
   */
  keys() {
    return this.params.keys();
  }

  /**
   * @param {string} name
   * @param {any} value
   */
  set(name, value) {
    this.params.set(name, [value]);
  }

  // Sort all key/value pairs based on the keys
  sort() {
    const sortedParams = new Map([...this.params.entries()].sort());
    this.params = sortedParams;
  }

  /**
   * @return {string}
   */
  toString() {
    const paramStrings = [];
    for (const [key, values] of this.params) {
      for (const value of values) {
        paramStrings.push(
          `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
        );
      }
    }
    return paramStrings.join("&");
  }

  /**
   * @return {Iterator} values
   */
  values() {
    const allValues = [];
    for (const [, values] of this.params) {
      allValues.push(...values);
    }
    return allValues.values();
  }
}

// Example usage:
const params = new URLSearchParams("?a=1&a=2&b=2");
console.log(params.get("a")); // Output: '1'
console.log(params.getAll("a")); // Output: ['1', '2']
console.log(params.get("b")); // Output: '2'
console.log(params.getAll("b")); // Output: ['2']

params.append("a", 3);
params.set("b", "3");
params.sort();
console.log(params.toString()); // Output: 'a=1&a=2&a=3&b=3'

57.异步任务队列

// 依次顺序执行一系列任务
// 所有任务全部完成后可以得到每个任务的执行结果
// 需要返回两个方法,start用于启动任务,pause用于暂停任务
// 每个任务具有原子性,即不可中断,只能在两个任务之间中断
const lastTimestamp = new Date();
function processTasks(...tasks) {
  let paused = true;
  let currentTaskIndex = 0;
  let results = [];

  function start() {
    return new Promise(async (resolve) => {
      if (!paused) return;
      paused = false;
      while (currentTaskIndex < tasks.length) {
        console.log(
          `Task${currentTaskIndex} start:`,
          new Date() - lastTimestamp
        );
        try {
          const result = await tasks[currentTaskIndex]();
          results.push(result);
        } catch (error) {
          results.push(error.message ?? error);
        }

        console.log(
          `Task${currentTaskIndex} execute done:`,
          new Date() - lastTimestamp
        );

        currentTaskIndex++;
        if (paused) {
          return;
        }
      }
      paused = true;
      resolve(results);
    });
  }

  function pause() {
    paused = true;
    console.log("paused at:", new Date() - lastTimestamp);
  }

  return { start, pause };
}

const request = (index, ms) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(index);
    }, ms);
  });
};

const fail = (index, ms) => {
  return new Promise((resolve, reject) => {
    throw Error('error');
  });
};

const reject = (index, ms) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject("reject");
    }, ms);
  });
};

const processor = processTasks(
  () => request(1, 2000),
  () => request(2, 2000),
  () => fail(3, 2000),
  () => reject(4, 2000),
  () => request(5, 2000)
);

processor.start();

setTimeout(() => {
  processor.start();
}, 0);

setTimeout(() => {
  processor.pause();
}, 1500);

setTimeout(async () => {
  console.log("resume at:", new Date() - lastTimestamp);
  const res = await processor.start();
  console.log("res:", res);
}, 3500);

// Task0 start: 0
// paused at: 1506
// Task0 execute done: 2005
// resume at: 3505
// Task1 start: 3505
// Task1 execute done: 5507
// Task2 start: 5507
// Task2 execute done: 5508
// Task3 start: 5508
// Task3 execute done: 7509
// Task4 start: 7510
// Task4 execute done: 9511
// res: [ 1, 2, 'error', 'reject', 5 ]

58.事件注册队列

class AsyncQueue {
  constructor() {
    this.events = {};
  }

  tap(name, fn) {
    if (!this.events[name]) {
      this.events[name] = [];
    }
    this.events[name].push(fn);
  }

  exec(name, finalCallback) {
    const tasks = this.events[name];
    // 如果没有回调函数列表或列表为空,则调用最终回调
    if (!tasks || tasks.length === 0) {
      return finalCallback();
    }

    // 定义一个执行任务的函数
    const executeTask = (index) => {
      // 如果所有任务已执行完毕,调用最终回调
      if (index === tasks.length) {
        return finalCallback();
      } // 获取当前要执行的任务
      const task = tasks[index];
      // 执行当前任务,并传递一个函数作为参数,当调用它时,执行下一个任务
      task(() => executeTask(index + 1));
    };

    executeTask(0);
  }
}

function fn1(cb) {
  console.log("fn1 time spent: ", new Date() - lastTimeStamp);
  cb();
}

function fn2(cb) {
  console.log("fn2 time spent: ", new Date() - lastTimeStamp);
  cb();
}

function fn3(cb) {
  setTimeout(() => {
    console.log("fn3 time spent: ", new Date() - lastTimeStamp);
    cb();
  }, 2000);
}

function fn4(cb) {
  setTimeout(() => {
    console.log("fn4 time spent: ", new Date() - lastTimeStamp);
    cb();
  }, 3000);
}

// 创建事件队列
const asyncQueue = new AsyncQueue();

// 注册事件队列
asyncQueue.tap("init", fn1);
asyncQueue.tap("init", fn2);
asyncQueue.tap("init", fn3);
asyncQueue.tap("init", fn4);

const lastTimeStamp = new Date();

// 执行事件队列
asyncQueue.exec("init", () => {
  console.log("执行结束");
});

// 预期输出
// fn1 time spent:  0
// fn2 time spent:  2
// fn3 time spent:  2005
// fn4 time spent:  5007
// 执行结束

59.treeToArray

function treeToArray(tree) {
  const result = [];

  function traverse(node) {
    result.push({ id: node.id, parentId: node.parentId, name: node.name });

    for (const child of node.children) {
      traverse(child);
    }
  }

  for (const node of tree) {
    traverse(node);
  }

  return result;
}

// 示例树形结构
const inputTree = [
  {
    id: 1,
    parentId: null,
    name: "Node 1",
    children: [
      {
        id: 2,
        parentId: 1,
        name: "Node 1.1",
        children: [
          {
            id: 4,
            parentId: 2,
            name: "Node 1.1.1",
            children: [],
          },
        ],
      },
      {
        id: 3,
        parentId: 1,
        name: "Node 1.2",
        children: [],
      },
    ],
  },
  {
    id: 5,
    parentId: null,
    name: "Node 2",
    children: [
      {
        id: 6,
        parentId: 5,
        name: "Node 2.1",
        children: [],
      },
    ],
  },
];

// 转换树形结构为数组
const array = treeToArray(inputTree);

console.log(array);

60.arrayToTree

function arrayToTree(arr) {
  const map = {};
  const result = [];

  // 构建节点映射表
  for (const item of arr) {
    const id = item.id;
    map[id] = { ...item, children: [] };
  }

  // 构建树形结构
  for (const item of arr) {
    const parent = map[item.parentId];
    if (parent) {
      parent.children.push(map[item.id]);
    } else {
      result.push(map[item.id]);
    }
  }

  return result;
}

// 示例数组
const inputArray = [
  { id: 1, parentId: null, name: "Node 1" },
  { id: 2, parentId: 1, name: "Node 1.1" },
  { id: 3, parentId: 1, name: "Node 1.2" },
  { id: 4, parentId: 2, name: "Node 1.1.1" },
  { id: 5, parentId: null, name: "Node 2" },
  { id: 6, parentId: 5, name: "Node 2.1" },
];

// 转换数组为树形结构
const tree = arrayToTree(inputArray);

console.log(tree);

61.版本号比较

function compareVersions(version1, version2) {
  const version1Parts = version1.split(".").map(Number);
  const version2Parts = version2.split(".").map(Number);

  const maxLength = Math.max(version1Parts.length, version2Parts.length);

  for (let i = 0; i < maxLength; i++) {
    const part1 = version1Parts[i] || 0;
    const part2 = version2Parts[i] || 0;

    if (part1 < part2) {
      return -1;
    } else if (part1 > part2) {
      return 1;
    }
  }

  return 0; // 版本号相等
}

// 示例
const result1 = compareVersions("1.2.3", "1.2.4");
const result2 = compareVersions("1.10", "1.2");
const result3 = compareVersions("2.0", "2.0.0");

console.log(result1); // 输出 -1
console.log(result2); // 输出 1
console.log(result3); // 输出 0

62.set(object, path, value)

function set(object, path, value) {
  const keys = path.split(".");
  let currentObject = object;

  for (let i = 0; i < keys.length - 1; i++) {
    const key = keys[i];
    if (!currentObject[key]) {
      currentObject[key] = {};
    }
    currentObject = currentObject[key];
  }

  const finalKey = keys[keys.length - 1];
  currentObject[finalKey] = value;
}

// 示例对象
const myObject = {};

// 使用 set 函数设置值到路径 "a.b.c"
set(myObject, "a.b.c", 42);

console.log(myObject); // 输出 { a: { b: { c: 42 } } }

63.get(object, path, value)

function get(object, path) {
  const keys = path.split(".");
  let currentObject = object;

  for (const key of keys) {
    if (currentObject && currentObject.hasOwnProperty(key)) {
      currentObject = currentObject[key];
    } else {
      return undefined; // 如果路径不存在,返回 undefined
    }
  }

  return currentObject;
}

// 示例对象
const myObject = {
  a: {
    b: {
      c: 42,
    },
  },
};

// 使用 get 函数获取路径 "a.b.c" 上的值
const result = get(myObject, "a.b.c");

console.log(result); // 输出 42

64.日期格式化

function dateFormat(date, format) {
  const year = date.getFullYear();
  const month = date.getMonth() + 1;
  const day = date.getDate();

  return format
    .replace(/yyyy/g, year)
    .replace(/MM/g, (month < 10 ? "0" : "") + month)
    .replace(/dd/g, (day < 10 ? "0" : "") + day);
}

console.log(dateFormat(new Date("2023-12-01"), "yyyy/MM/dd")); // 2023/12/01
console.log(dateFormat(new Date("2023-10-01"), "yyyy/MM/dd")); // 2023/10/01
console.log(dateFormat(new Date("2023-04-01"), "yyyy年MM月dd日")); // 2023年04月01日

65.双向绑定

function twoWayBinding(state, element) {
  // 将状态对象中的值赋给输入框的初始值
  element.value = state.value;

  // 监听输入框的变化事件,更新状态对象的值
  element.addEventListener("input", function () {
    state.value = element.value;
  });

  // 监听状态对象的变化,更新输入框的值
  Object.defineProperty(state, "value", {
    set: function (newValue) {
      element.value = newValue;
    },
    get: function () {
      return element.value;
    },
  });
}

const input = document.createElement("input");
const state = { value: "BFE" };
twoWayBinding(state, input);

console.log(input.value); // 输出 'BFE'
state.value = "dev";
console.log(input.value); // 输出 'dev'
input.value = "BFE.dev";
input.dispatchEvent(new Event("input")); // 模拟输入框变化事件
console.log(state.value); // 输出 'BFE.dev'

66.插值表达式

function render(template, data) {
  const reg = /\{\{(\w+)\}\}/; // 模板字符串正则
  if (reg.test(template)) { // 判断模板里是否有模板字符串
    const name = reg.exec(template)[1]; // 查找当前模板里第一个模板字符串的字段
    template = template.replace(reg, data[name]); // 将第一个模板字符串渲染
    return render(template, data); // 递归的渲染并返回渲染后的结构
  }
  return template; // 如果模板没有模板字符串直接返回
}

let template = "我是{{name}},年龄{{age}},性别{{sex}}";

let data = {
  name: "姓名",
  age: 18,
};

console.log(render(template, data)); // 我是姓名,年龄18,性别undefined

67.binarySearch

function binarySearch(arr, target) {
  let left = 0;
  let right = arr.length - 1;

  while (left <= right) {
    const mid = Math.floor((left + right) / 2);

    if (arr[mid] === target) {
      return mid; // 找到目标元素,返回索引
    } else if (arr[mid] < target) {
      left = mid + 1; // 目标元素在右侧
    } else {
      right = mid - 1; // 目标元素在左侧
    }
  }

  return -1; // 目标元素不存在于数组中
}

68.Math.sqrt

function sqrt(x) {
  let left = 0;
  let right = x;

  while (left <= right) {
    let mid = left + Math.floor((right - left) / 2);

    if (mid * mid === x) {
      return mid;
    } else if (mid * mid < x) {
      left = mid + 1;
    } else {
      right = mid - 1;
    }
  }

  return right;
}

console.log(sqrt(9)); // 3
console.log(sqrt(27)); // 5

69.Math.pow

// 迭代版本
function pow(x, y) {
  let result = 1;

  for (let i = 0; i < y; i++) {
    result *= x;
  }

  return result;
}

// 递归版本
function pow(x, y) {
  if (y === 0) return 1;
  return x * pow(x, y - 1);
}

// logn版本
function pow(x, y) {
  if (y === 0) return 1;

  let half = pow(x, Math.floor(y / 2));

  if (y % 2 === 0) {
    return half * half;
  } else {
    return half * half * x;
  }
}

70.isPrime

function isPrime(number) {
  // 质数必须是大于1的整数
  if (number <= 1 || !Number.isInteger(number)) {
    return false;
  }

  // 2是唯一的偶数质数
  if (number === 2) {
    return true;
  }

  // 排除所有的偶数
  if (number % 2 === 0) {
    return false;
  }

  // 从3开始,逐一检查是否有除1和自身以外的因子
  for (let i = 3; i <= Math.sqrt(number); i += 2) {
    if (number % i === 0) {
      return false;
    }
  }

  return true;
}

71. TodoList

import React, { useState } from "react";

function useTodoList() {
  const [todos, setTodos] = useState([]);
  const [past, setPast] = useState([]);
  const [future, setFuture] = useState([]);

  // 添加 todo
  function add(text) {
    setPast((p) => [...p, todos]); // 先把当前状态存到历史
    setTodos((t) => [...t, { id: Date.now(), text }]);
    setFuture([]); // 新操作清空未来
  }

  // 删除 todo
  function remove(id) {
    setPast((p) => [...p, todos]);
    setTodos((t) => t.filter((todo) => todo.id !== id));
    setFuture([]);
  }

  // 撤销
  function undo() {
    if (past.length === 0) return;
    const previous = past[past.length - 1];
    setPast((p) => p.slice(0, p.length - 1));
    setFuture((f) => [todos, ...f]);
    setTodos(previous);
  }

  // 重做
  function redo() {
    if (future.length === 0) return;
    const next = future[0];
    setFuture((f) => f.slice(1));
    setPast((p) => [...p, todos]);
    setTodos(next);
  }

  return {
    todos,
    add,
    remove,
    undo,
    redo,
    canUndo: past.length > 0,
    canRedo: future.length > 0,
  };
}

// 支持添加/删除待办事项
// 支持撤销(undo)/重做(redo)操作
// 每次状态变更必须使用不可变数据
// 使用函数组件编写
export function TodoList() {
  // implementation
  const { todos, add, remove, undo, redo, canUndo, canRedo } = useTodoList();
  const [input, setInput] = useState("");

  function addTodo() {
    if (!input.trim()) return;
    add(input.trim());
    setInput("");
  }

  return (
    <div style={{ padding: 20 }}>
      <input
        value={input}
        onChange={(e) => setInput(e.target.value)}
        onKeyDown={(e) => e.key === "Enter" && addTodo()}
        placeholder="Add todo..."
      />
      <button onClick={undo} disabled={!canUndo}>
        Undo
      </button>
      <button onClick={redo} disabled={!canRedo}>
        Redo
      </button>
      <ul>
        {todos.map(({ id, text }) => (
          <li key={id}>
            {text} <button onClick={() => remove(id)}>❌</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

72.模版字符串

const name = "John";
const age = 25;
const greeting = templateString`Hello, my name is ${name} and I'm ${age} years old.`;

function templateString(strings, ...values) {
  return strings.reduce(
    (result, str, i) => result + str + (i < values.length ? values[i] : ""),
    "",
  );
}

console.log(greeting); // Output: Hello, my name is John and I'm 25 years old.

73.proxy

function createProxy(value = 0) {
    return new Proxy({}, {
        get(target, prop) {
            if (prop === Symbol.toPrimitive) {
                return () => value;
            }
            return createProxy(value + Number(prop));
        }
    })
}

const add = createProxy();

const r1 = add[1][2][3] * 4
const r2 = add[10][20] + 30
const r3 = add[100][200][300] + 400

console.log(r1) // 24
console.log(r2) // 60
console.log(r3) // 1000

74.range

// 实现 Array.range 函数
Array.prototype.range = function (start, count) {
  // 使用 Array.from 生成一个包含 'count' 个元素的数组,然后映射每个元素为 start + index
  return Array.from({ length: count }, (_, index) => start + index);
};

// 实现 Array.prototype.sum 方法
Array.prototype.sum = function () {
  // 使用 reduce 方法将数组中的所有元素累加起来
  return this.reduce((acc, currentValue) => acc + currentValue, 0);
};

console.log(Array.range(0, 3)); // 输出 [0, 1, 2]

const sumArray = [0, 1, 2];
const sum = sumArray.sum();
console.log(sum); // 输出 3

console.log(Array.range(-1, 4).sum()); // 输出 2

75.query

// Query类,用于链式查询
class Query {
  // 构造函数,接收初始数组
  constructor(list) {
    // 需要操作的数组
    this.list = list;
    // 操作结果
    this.result = {};
  }

  // 过滤数组
  where = (fn) => {
    // 筛选数组
    this.list = this.list.filter(fn);
    // 返回this,用于链式调用
    return this;
  };

  // 根据某字段排序
  sortBy = (field) => {
    // 根据字段排序数组
    this.list = this.list.sort((a, b) => a[field] - b[field]);
    return this;
  };

  // 根据某字段分组
  groupBy = (field) => {
    // 分组的结果对象
    const obj = {};
    // 遍历数组
    this.list.forEach((item) => {
      // 获取分组字段的值
      const fieldValue = item[field];
      // 判断是否已经有该分组
      if (obj[fieldValue]) {
        // 如果有,添加到数组
        obj[fieldValue].push(item);
      } else {
        // 如果没有,创建数组
        obj[fieldValue] = [item];
      }
    });
    // 保存分组结果
    this.result = obj;
    return this;
  };

  // 返回最终结果
  execute = () => {
    return this.result;
  };
}

const list = [
  { id: 2, age: 20, type: "web" },
  { id: 3, age: 22, type: "web" },
  { id: 6, age: 10, type: "ios" },
  { id: 1, age: 30, type: "ios" },
  { id: 7, age: 12, type: "java" },
];

// 创建查询实例
const query = (list) => {
  return new Query(list);
};

// 示例
const result = query(list)
  .where((item) => item.age > 18)
  .sortBy("id")
  .groupBy("type")
  .execute();

console.log(result); // {"ios":[{"id":1,"age":30,"type":"ios"}],"web":[{"id":2,"age":20,"type":"web"},{"id":3,"age":22,"type":"web"}]}

76.最快找出最短RTT的IP地址

async function findShortestRTT(ips, parallelCount = 10) {
    // [[ip1,ip2,ip3],[ip4,ip5,ip6]...]
    const ipChunks = chunk(ips, parallelCount);
    let result = {
        rtt: Infinity,
        ip: ''
    }
    for (const chunk of ipChunks) {
        const temp = await race(chunk, result.rtt)
        if (temp) {
            result = temp
        }
    }
    return result.ip
}

async function race(ips, maxTime) {
    return new Promise((resolve) => {
        const controller = new AbortController();
        const signal = controller.signal;
        const start = Date.now();
        for (const ip of ips) {
            fetch(`http://${ip}/ping`, { signal }).then(() => {
                const rtt = Date.now() - start;
                resolve({ rtt, ip })
                // 取消所有请求
                controller.abort()
            })
        }
        setTimeout(() => {
            resolve(null)
            // 取消所有请求
            controller.abort()
        }, maxTime)
    })
}

function chunk(arr, size) {
    const result = [];
    for (let i = 0; i < arr.length; i += size) {
        result.push(arr.slice(i, i + size));
    }
    return result;
}

77.zipWith

function zipWith(fn, a0, a1) {
  // 初始化一个空数组,用于存放结果
  const result = [];

  // 遍历输入的两个数组,长度取两者中较小的长度
  const length = Math.min(a0.length, a1.length);
  for (let i = 0; i < length; i++) {
    // 对两个数组中相同位置的元素应用给定的函数,并将结果添加到结果数组中
    result.push(fn(a0[i], a1[i]));
  }

  return result;
}

// 示例用法
console.log(zipWith(Math.pow, [10, 10, 10, 10], [0, 1, 2, 3])); // 输出 [1, 10, 100, 1000]
console.log(zipWith(Math.max, [1, 4, 7, 1, 4, 7], [4, 7, 1, 4, 7, 1])); // 输出 [4, 7, 7, 4, 7, 7]

// 使用匿名函数或箭头函数来执行加法操作
console.log(
  zipWith(
    function (a, b) {
      return a + b;
    },
    [0, 1, 2, 3],
    [0, 1, 2, 3]
  )
); // 输出 [0, 2, 4, 6]
console.log(zipWith((a, b) => a + b, [0, 1, 2, 3], [0, 1, 2, 3])); // 输出 [0, 2, 4, 6]

78.mergePromise

const request = (index, ms) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(index);
    }, ms);
  });
};

const times = [1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000];
const promises = [];
const lastTimeStamp = new Date();

for (let i = 0; i < 10; i++) {
  promises.push(() => request(i, times[i]));
}

// 串行执行Promise,而不是Promise.all并行执行
mergePromise(promises).then((data) => {
  console.log(data);
});

// task0 done, time spent: 1002
// task1 done, time spent: 2006
// task2 done, time spent: 3007
// task3 done, time spent: 4009
// task4 done, time spent: 5010
// task5 done, time spent: 6012
// task6 done, time spent: 7013
// task7 done, time spent: 8014
// task8 done, time spent: 9016
// task9 done, time spent: 10018
// [
//   0, 1, 2, 3, 4,
//   5, 6, 7, 8, 9
// ]
function mergePromise(promises) {
  const data = []; // 存储每个Promise的结果
  let promise = Promise.resolve(); // 初始Promise

  promises.forEach((requestFn, index) => {
    // 串行执行每个promiseFunc
    promise = promise.then(() => {
      return requestFn().then((result) => {
        console.log(`task${index} done, time spent:`, new Date() - lastTimeStamp);
        data.push(result); // 收集结果
        return data; // 传递累积结果到下一个then
      });
    });
  });

  // 当所有的promises都执行完毕,返回包含所有结果的数组
  return promise;
}

function mergePromise(promiseFactories) {
  // 使用reduce串行执行Promise
  return promiseFactories.reduce((prevPromise, nextPromiseFactory, index) => {
    return prevPromise.then((result) => {
      // result是累积的结果数组,首次调用是空数组
      return nextPromiseFactory().then((currentResult) => {
        console.log(`task${index} done, time spent:`, new Date() - lastTimeStamp);
        // 将当前Promise的结果添加到累积结果数组中
        return result.concat(currentResult);
      });
    });
  }, Promise.resolve([])); // 初始值为解决状态的Promise,带有空数组
}

79.千分位分割

function formatNumberWithCommas(number) {
  // 将数字转换为字符串,并按小数点分割
  const parts = number.toString().split(".");

  // 处理整数部分
  let integerPart = parts[0];
  let formattedInteger = "";
  let count = 0;

  for (let i = integerPart.length - 1; i >= 0; i--) {
    count++;
    formattedInteger = integerPart[i] + formattedInteger;
    if (count % 3 === 0 && i !== 0) {
      formattedInteger = "," + formattedInteger;
    }
  }

  // 处理小数部分
  const decimalPart = parts[1] || "";

  // 返回格式化后的数字
  return formattedInteger + (decimalPart.length > 0 ? "." + decimalPart : "");
}

// 使用示例
const numberWithCommas = formatNumberWithCommas(1234567890.123456);
console.log(numberWithCommas); // 输出 "1,234,567,890.123456"

//无小数点
let num1 = "1321434322222";
console.log(num1.replace(/(\d)(?=(\d{3})+$)/g, "$1,")); // 1,321,434,322,222

//有小数点
let num2 = "342243242322.3432423";
console.log(num2.replace(/(\d)(?=(\d{3})+\.)/g, "$1,")); // 342,243,242,322.3432423