前言
大厂前端社招面试的一二面,大致是这么分配时间的,半小时的自我介绍+八股文(一面)/项目(二面),半小时做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