本文将常见手写真题汇总,便于大家刷题使用。本文内容包括:
- Promise系列
- 数组系列
- JS原理系列
- 函数式编程系列
- 等等
Promise系列
手写Promise
class MyPromise {
constructor(executor) {
this.value = undefined;
this.reason = undefined;
this.status = 'pending';
const resolve = (value) => {
if (this.status === 'pending') {
this.value = value;
this.status = 'resolved';
}
};
const reject = (reason) => {
if (this.status === 'pending') {
this.reason = reason;
this.status = 'rejected';
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
if (this.status === 'resolved') {
onFulfilled(this.value);
} else if (this.status === 'rejected') {
onRejected(this.reason);
}
}
catch(onRejected) {
if (this.status === 'rejected') {
onRejected(this.reason);
}
}
}
手写Promise.all
function promiseAll(promises) {
return new Promise((resolve, reject) => {
const results = [];
let completedCount = 0;
// 没有任务
if(!promises.length) {
return resolve(results)
}
promises.forEach((promise, i) => {
Promise.resolve(promise)
.then(result => {
completedCount ++;
results[i] = result;
if(completedCount === promises.length) {
resolve(results);
}
})
.catch(error => {
reject(error)
})
});
});
}
手写Promise.race
function promiseRace(promises) {
return new Promise((resolve, reject) => {
promises.forEach(p => {
// 让每个任务都变成Promise任务。等他们任何一个执行完就会走到then和catch结束
Promise.resolve(p).then(resolve).catch(reject);
})
});
}
手写defer
class Deferred {
constructor() {
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}
}
手写Promise.allSettled
function promiseAllSettled(promises) {
return Promise.all(
promises.map((promise) =>
promise
.then((value) => ({
status: "fulfilled",
value: value,
}))
.catch((reason) => ({
status: "rejected",
reason: reason,
}))
)
);
}
手写Promise.map
function promiseMap(promises) {
return Promise.all(promises.map((promise) => promise.catch((error) => error)));
}
手写Promise.retry
function promiseRetry(asyncFn, maxRetries) {
return new Promise((resolve, reject) => {
let retries = 0;
function attempt() {
asyncFn()
.then(resolve)
.catch((error) => {
retries++;
if (retries <= maxRetries) {
attempt();
} else {
reject(error);
}
});
}
attempt();
});
}
手写异步串行任务
function series(tasks) {
return tasks.reduce((promiseChain, currentTask) => {
return promiseChain.then((result) => {
return currentTask().then((currentResult) => {
return result.concat(currentResult);
});
});
}, Promise.resolve([]));
}
手写批量请求
function batchRequest(requests, maxConcurrency) {
return new Promise((resolve, reject) => {
const results = [];
let completedCount = 0;
let currentIndex = 0;
function makeRequest(index) {
if (index >= requests.length) {
if (completedCount === requests.length) {
resolve(results);
}
return;
}
const currentRequest = requests[index];
currentRequest()
.then((result) => {
results[index] = result;
})
.catch((error) => {
results[index] = error;
})
.finally(() => {
completedCount++;
makeRequest(currentIndex++);
});
if (currentIndex - index < maxConcurrency) {
makeRequest(currentIndex++);
}
}
makeRequest(0);
});
}
手写Promise.resolve
static resolve(value: any) {
if (value instanceof Promise2) return value
return new Promise2((resolve: Function) => {
resolve(value)
})
}
手写Promise.finally
// 1. 调用当前 Promise 的 then 方法返回一个新的 Promise 对象(保证链式调用)
// 2. 调用 Promise 中的 resolve 方法进行返回
public finally(callback: Function) {
return this.then(
(value: any) => this.resolve(callback()).then(() => value),
(reason: any) =>
this.resolve(callback()).then(() => {
throw reason;
})
);
}
手写Promise.catch
public catch(cb: Function) {
this.then(undefined, cb)
}
数组系列
数组扁平化flat
function flat(arr) {
return arr.reduce((result, item) => {
return result.concat(Array.isArray(item) ? flat(item) : item);
}, []);
}
数组去重
function unique(arr) {
// return array.filter((it, i) => array.indexOf(it) === i)
const result = [];
for (const item of arr) {
if (!result.includes(item)) {// 可以换成indexOf filter等
result.push(item);
}
}
return result;
}
手写reduce
function reduce(arr, reducer, initialValue) {
let accumulator = initialValue;
let start = 0;
if(initialValue == null) {// 没传初始值的处理
accumulator = arr[0];
start = 1;
}
for (let i = start; i < arr.length; i++) {
accumulator = reducer(accumulator, arr[i], i, arr);
}
return accumulator;
}
手写数组转树
type Item = {
[key: string]: any
}
const foo = (list: Item[]) => {
const map: Record<string, Item> = {}
const treeList: Item[] = []
list.forEach(item => {
if(!item.children) item.children = [];
map[item.id] = item
});
list.forEach(item => {
const parent = map[item.parentId];
if(parent) {
parent.children.push(item);
} else {
treeList.push(item);
}
});
return treeList;
}
const arr = [
{
id: 2,
name: '部门B',
parentId: 0
},
{
id: 3,
name: '部门C',
parentId: 1
},
{
id: 1,
name: '部门A',
parentId: 2
},
{
id: 4,
name: '部门D',
parentId: 1
},
{
id: 5,
name: '部门E',
parentId: 2
},
{
id: 6,
name: '部门F',
parentId: 3
},
{
id: 7,
name: '部门G',
parentId: 2
},
{
id: 8,
name: '部门H',
parentId: 4
}
]
console.log(foo(arr))
export default foo
手写filter/map/every
// filter 方法实现
Array.prototype.myFilter = function(callback) {
const filteredArray = [];
// 遍历数组元素
for (let i = 0; i < this.length; i++) {
// 根据回调函数的返回值进行过滤
if (callback(this[i], i, this)) {
filteredArray.push(this[i]); // 符合条件的元素加入结果数组
}
}
return filteredArray; // 返回过滤后的新数组
};
// map 方法实现
Array.prototype.myMap = function(callback) {
const mappedArray = [];
// 遍历数组元素
for (let i = 0; i < this.length; i++) {
// 对每个元素进行映射操作,并将结果加入新数组
mappedArray.push(callback(this[i], i, this));
}
return mappedArray; // 返回映射后的新数组
};
// every 方法实现
Array.prototype.myEvery = function(callback) {
// 遍历数组元素
for (let i = 0; i < this.length; i++) {
// 如果有任何元素不满足条件,则返回 false
if (!callback(this[i], i, this)) {
return false;
}
}
return true; // 所有元素都满足条件,返回 true
};
函数式编程系列
手写柯里化
const curry = (fn, ...args) => {
if (args.length < fn.length) {
// 未接受完参数,拼上参数
return (..._args) => curry(fn, ...args, ..._args)
} else {
// 接受完所有参数,直接执行
return fn(...args)
}
}
const add = (a, b, c) => {
return a + b + c
}
const curried = curry(add)
console.log(curried(1)(2)(3)) // 输出 6
console.log(curried(1, 2)(3)) // 输出 6
console.log(curried(1)(2, 3)) // 输出 6
手写compose
function compose(...functions) {
return function(init) {
let result = init;
for (let i = functions.length - 1; i >= 0; i--) {
result = functions[i](result);
}
return result;
};
}
JS原理系列
手写EventEmitter
type Listener = (...args: any[]) => void;
class EventEmitter {
private events: Record<string, Listener[]> = {};
public on(eventName: string, listener: Listener): void {
if (!this.events[eventName]) {
this.events[eventName] = [];
}
this.events[eventName].push(listener);
}
public emit(eventName: string, ...args: any[]): void {
const listeners = this.events[eventName];
if (listeners) {
listeners.forEach(listener => listener(...args));
}
}
public off(eventName: string, listener: Listener): void {
const listeners = this.events[eventName];
if (listeners) {
this.events[eventName] = listeners.filter(l => l !== listener);
}
}
public once(eventName: string, listener: Listener): void {
const wrapper = (...args: any[]) => {
listener(...args);
this.off(eventName, wrapper);
};
this.on(eventName, wrapper);
}
}
export default EventEmitter
手写防抖函数debounce
function debounce(fn, delay) {
let timer = null
return function (...args) {
clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}
const Component = () => {
const onClick = debounce(() => {
console.log('防抖')
}, 1000);
return <button onClick={onClick}>测试</button>
}
export default Component;
手写节流函数throttle
function throttle(fn, delay) {
let start = +Date.now()
let timer = null
return function(...args) {
const now = +Date.now()
if (now - start >= delay) {
clearTimeout(timer)
timer = null
fn.apply(this, args)
start = now
} else if (!timer){
timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}
}
// 计算时间的,所以第一次会执行
const throtte2 = (fn, wait) => {
let pre = 0;
return function() {
const now = new Date().getTime();
if(now - pre > wait) {
fn();
pre = now;
}
}
}
const Component = () => {
const onClick = throtte2(() => {
console.log('节流')
}, 1000);
return <button onClick={onClick}>测试</button>
}
export default Component;
手写instanceOf
function myInstanceOf(obj, constructor) {
// 加上异常处理
if(['object', 'function'].includes(typeof obj) || obj === null) return false;// 非有效对象\函数
let proto = Object.getPrototypeOf(obj);
while (proto !== null) {
if (proto === constructor.prototype) {
return true;
}
proto = Object.getPrototypeOf(proto);
}
return false;
}
手写Object.create
function createObject(prototype) {
function Temp() {} // 创建一个空的构造函数
Temp.prototype = prototype; // 将原型对象赋值给构造函数的原型
return new Temp(); // 使用构造函数创建一个新对象
}
setTimeout模拟setInterval
function simulatedSetInterval(callback, timeout) {
let timer = null;
function interval() {
timer = setTimeout(() => {
callback();
interval()
}, timeout);
}
interval();
return () => clearTimout(timer);
}
setInterval模拟setTimeout
function simulatedSetTimeout(callback, delay) {
let timer = 0;
timer = setInterval(function() {
clearInterval(timer);
callback();
}, delay);
return () => clearInterval(timer);
}
对象扁平化
const flattern = (obj) => {
const res = {};
const dfs = (curr, path) => {
if(typeof curr === 'object' && curr !== null) {
const isArray = Array.isArray(curr);
for(let key in curr) {
const newPath = path ? isArray ? `${path}[${key}]` : `${path}.${key}` : key;
dfs(curr[key], newPath);
}
} else {
res[path] = curr
}
}
dfs(obj);
return res;
}
手写call
Function.prototype.myCall = function (context, ...args) {
context = context || window;
const fn = Symbol('fn');
context[fn] = this;// 将myCall方法绑定
const result = context[fn](...args);
delete context[fn];
return result;
};
手写apply
Function.prototype.myApply = function (context, args) {
context = context || window;
const fn = Symbol('fn');
context[fn] = this;
const result = context[fn](...args);
delete context[fn];
return result;
};
手写bind
Function.prototype.myBind = function (context, ...args) {
const self = this;
return function (...innerArgs) {
return self.apply(context, args.concat(innerArgs));
};
};
手写深拷贝
function deepClone(obj, clonedMap = new WeakMap()) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
// 缓存避免循环引用
if (clonedMap.has(obj)) {
return clonedMap.get(obj);
}
// 正则表达式对象
if (obj instanceof RegExp) {
return new RegExp(obj);
}
// 日期对象
if (obj instanceof Date) {
return new Date(obj.getTime());
}
let clone = Array.isArray(obj) ? [] : {};
clonedMap.set(obj, clone);
// 递归拷贝
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key], clonedMap);
}
}
return clone;
}