前言
在面试里常遇到的面试题和手写题进行阶段总结。
模拟实现 Promise.all Promise.race Promise.allSettled
JavaScript
// 模拟实现Promise.all
function customPromiseAll(promises) {
return new Promise((resolve, reject) => {
let results = [];
let completedPromises = 0;
promises.forEach((promise, index) => {
Promise.resolve(promise)
.then((result) => {
results[index] = result;
completedPromises++;
if (completedPromises === promises.length) {
resolve(results);
}
})
.catch((error) => {
reject(error);
});
});
});
}
// 模拟实现Promise.race
function customPromiseRace(promises) {
return new Promise((resolve, reject) => {
promises.forEach((promise) => {
Promise.resolve(promise)
.then((result) => {
resolve(result);
})
.catch((error) => {
reject(error);
});
});
});
}
// 模拟实现Promise.allSettled
function customPromiseAllSettled(promises) {
return new Promise((resolve) => {
let settledPromises = [];
let completedPromises = 0;
promises.forEach((promise, index) => {
Promise.resolve(promise)
.then((result) => {
settledPromises[index] = { status: 'fulfilled', value: result };
})
.catch((error) => {
settledPromises[index] = { status: 'rejected', reason: error };
})
.finally(() => {
completedPromises++;
if (completedPromises === promises.length) {
resolve(settledPromises);
}
});
});
});
}
模拟实现 instanceof
JavaScript
function customInstanceof(obj, constructorFunc) {
// 获取对象的原型
let proto = Object.getPrototypeOf(obj);
// 获取构造函数的原型
let prototype = constructorFunc.prototype;
// 循环向上查找原型链
while (proto !== null) {
// 如果找到了匹配的原型,则返回 true
if (proto === prototype) {
return true;
}
// 向上查找原型链
proto = Object.getPrototypeOf(proto);
}
// 如果没有找到匹配的原型,则返回 false
return false;
}
// 测试示例
function Person(name) {
this.name = name;
}
let personObj = new Person('John');
console.log(customInstanceof(personObj, Person)); // 输出 true
console.log(customInstanceof(personObj, Object)); // 输出 true,因为所有对象都是Object的实例
console.log(customInstanceof(personObj, Array)); // 输出 false,因为personObj不是Array的实例
模拟实现 call、apply、bind
JavaScript
// 模拟实现 call 方法
Function.prototype.customCall = function(context, ...args) {
context = context || window;
const fn = Symbol();
context[fn] = this;
const result = context[fn](...args);
delete context[fn];
return result;
};
// 测试示例
function greet(message) {
return `${message}, ${this.name}!`;
}
const person = { name: 'John' };
console.log(greet.customCall(person, 'Hello')); // 输出 "Hello, John!"
// 模拟实现 apply 方法
Function.prototype.customApply = function(context, args) {
context = context || window;
const fn = Symbol();
context[fn] = this;
const result = context[fn](...args);
delete context[fn];
return result;
};
// 测试示例
function greet(message) {
return `${message}, ${this.name}!`;
}
const person = { name: 'John' };
console.log(greet.customApply(person, ['Hello'])); // 输出 "Hello, John!"
// 模拟实现 bind 方法
Function.prototype.customBind = function(context, ...args) {
const fn = this;
return function(...innerArgs) {
return fn.apply(context, args.concat(innerArgs));
};
};
// 测试示例
function greet(message) {
return `${message}, ${this.name}!`;
}
const person = { name: 'John' };
const boundGreet = greet.customBind(person, 'Hello');
console.log(boundGreet()); // 输出 "Hello, John!"
实现异步并发控制
JavaScript
class AsyncQueue {
constructor(concurrency) {
this.concurrency = concurrency; // 最大并发量
this.running = 0; // 当前并发量
this.queue = []; // 任务队列
}
async run() {
while (this.running < this.concurrency && this.queue.length > 0) {
const task = this.queue.shift();
this.running++;
task().then(() => {
this.running--;
this.run();
});
}
}
add(task) {
this.queue.push(task);
this.run();
}
}
// 使用示例
const asyncQueue = new AsyncQueue(2); // 最大并发量为2
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
function createTask(id, time) {
return async () => {
console.log(`Task ${id} started`);
await delay(time);
console.log(`Task ${id} finished`);
};
}
asyncQueue.add(createTask(1, 2000)); // 添加任务1,执行时间为2000ms
asyncQueue.add(createTask(2, 1000)); // 添加任务2,执行时间为1000ms
asyncQueue.add(createTask(3, 1500)); // 添加任务3,执行时间为1500ms
asyncQueue.add(createTask(4, 500)); // 添加任务4,执行时间为500ms
千位分隔数 - leetcode#1556
TypeScript
// 使用遍历常规解题
function thousandSeparator(n: number): string {
const len = nums.length;
// 处理长度不够三位的边缘场景
if (len < 4) return ('' + n);
// 转换为字符串
const nums = ('' + n).split('').reverse();
let res = '';
while (nums.length) {
const temp = nums.splice(0, 3).reverse();
res = '.' + temp.join('') + res;
}
return res.startsWith('.') ? res.slice(1) : res;
};
// 技巧解题
function thousandSeparator(n: number): string {
return n.toLocaleString().replaceAll(',', '.');
}
二叉树的层序遍历 - leetcode#102
TypeScript
function levelOrder(root: TreeNode | null): number[][] {
// 处理边缘场景
if (!root) {
return [];
}
let result = [];
let queue = [root];
// 对每一层做遍历
while (queue.length > 0) {
let level = [];
let size = queue.length;
for (let i = 0; i < size; i++) {
let node = queue.shift();
level.push(node.val);
if (node.left) {
queue.push(node.left);
}
if (node.right) {
queue.push(node.right);
}
}
result.push(level);
}
return result;
};
LRU 缓存 - leetcode#146
TypeScript
class LRUCache {
capacity;
cacheMap;
constructor(capacity: number) {
this.capacity = capacity;
this.cacheMap = new Map<number, number>();
}
get(key: number): number {
const has = this.cacheMap.has(key);
const value = this.cacheMap.get(key);
if (has) {
this.cacheMap.delete(key);
this.cacheMap.set(key, value);
}
return has ? value : -1;
}
put(key: number, value: number): void {
const size = this.cacheMap.size;
const has = this.cacheMap.has(key);
if (has) {
this.cacheMap.delete(key);
this.cacheMap.set(key, value);
} else {
if (size < this.capacity) {
this.cacheMap.set(key, value);
} else {
this.cacheMap.delete([...this.cacheMap.keys()][0]);
this.cacheMap.set(key, value);
}
}
}
}
最长公共前缀 - leetcode#14
TypeScript
function longestCommonPrefix(strs: string[]): string {
const p = strs[0];
let res = '';
let i = 0;
while (i < p.length) {
const str = p[i];
const has = strs.every((item) => item[i] === str);
if (has) {
res += str;
i++;
} else {
return res;
}
}
return res;
};
有效的括号 - leetcode#20
TypeScript
function isValid(s: string): boolean {
const left = ['(', '{', '['];
const right = [')', '}', ']'];
const com = ['()', '{}', '[]'];
const stack: string[] = [];
for (const str of s) {
if (left.includes(str)) {
stack.push(str);
}
if (right.includes(str)) {
const top = stack[stack.length - 1];
if (com.includes(top + str)) {
stack.pop();
} else {
return false;
}
}
}
return !stack.length;
};
三数之和 - leetcode#15
TypeScript
function threeSum(nums: number[]): number[][] {
const memory: string[] = [];
const twoSum = (nums: number[], target: number) => {
let i = 0;
let j = nums.length - 1;
nums.sort((a, b) => a - b);
const res: number[][] = [];
while(i < j) {
const value1 = nums[i];
const value2 = nums[j];
const sum = value1 + value2;
if (sum < target) i++;
if (sum >= target) j--;
if (sum === target) {
i++;
const temp = [value1, value2, 0 - target].sort((a, b) => a - b);
const tempString = temp.join();
if (!memory.includes(tempString)) {
res.push(temp);
memory.push(tempString);
}
}
}
return res;
}
const result: number[][] = [];
for (const [key, val] of nums.entries()) {
const temp = twoSum(nums.slice(key + 1), 0 - val);
if (temp.length) result.push(...temp);
}
return result;
};
最小栈 - leetcode#155
TypeScript
class MinStack {
stackNums = [];
constructor() {
}
push(val: number): void {
this.stackNums.push(val);
}
pop(): void {
return this.stackNums.pop();
}
top(): number {
return this.stackNums[this.stackNums.length - 1];
}
getMin(): number {
return Math.min(...this.stackNums);
}
}
比较版本号 - leetcode#165
TypeScript
function compareVersion(version1: string, version2: string): number {
let versions1 = version1.split('.');
let versions2 = version2.split('.');
const len1 = versions1.length;
const len2 = versions2.length;
const len = Math.max(len1, len2);
for (let i = 0; i < len; i++) {
if (Number(versions1[i] || 0) > Number(versions2[i] || 0)) return 1;
if (Number(versions1[i] || 0) === Number(versions2[i] || 0)) continue;
if (Number(versions1[i] || 0) < Number(versions2[i] || 0)) return -1;
}
return 0;
};