这篇文章包含了一些前端常见的手撕题,和部分我在面试过程中遇到的手撕题。非算法题。防抖function debounce(fn, delay, immediate = false) { let timer let flag = true return function () { if (flag && immediate) { fn.apply(this, arguments) flag = false return } clearTimeout(timer) timer = setTimeout(() => { fn.apply(this, arguments) }, delay) }}节流function throttle(fn, interval) { let last = 0 return function () { if (Date.now() - last < interval) return last = Date.now() fn.apply(this, arguments) }}柯里化function curry(fn, ...args1) { if (args1.length >= fn.length) { return fn(...args1) } else { return (...args2) => curry(fn, ...args1, ...args2) }}浅拷贝function shallowCopy(obj) { return Array.isArray(obj) ? [...obj] : { ...obj } // or return Object.assign(Array.isArray(obj) ? [] : {}, obj)}深拷贝function deepClone(o, wm = new WeakMap()) { if (wm.has(o)) return wm.get(o) let co = Array.isArray(o) ? [] : {} typeof o === 'object' && o !== null && wm.set(o, co) for (let k of Object.getOwnPropertySymbols(o)) { if (typeof o[k] === 'object' && o[k] !== null) { co[k] = deepClone(o[k], wm) } else { co[k] = o[k] } } for (let k in o) { if (o.hasOwnProperty(k)) { if (typeof o[k] === 'object' && o[k] !== null) { co[k] = deepClone(o[k], wm) } else { co[k] = o[k] } } } return co}数组最大深度function getDepth(ele) { if (!Array.isArray(ele)) { return 0 } let maxDepth = 0 ele.forEach(v => { maxDepth = Math.max(maxDepth, getDepth(v)) }) return maxDepth + 1}数组flatArray.prototype.myFlat = function (depth = 1) { if (depth <= 0) return [...this]; return this.reduce((p, c) => { if (Array.isArray(c)) { p.push(...c.myFlat(depth - 1)) } else { p.push(c) } return p }, [])}数组reduceArray.prototype.myReduce = function (cb, initial) { let res = initial !== undefined ? initial : this[0] let start = initial !== undefined ? 0 : 1 for (let i = start; i < this.length; ++i) { res = cb(res, this[i]) } return res}newfunction myNew(con, ...args) { if (!con instanceof Function) { throw new Error() } // let obj = {} // obj.__proto__ = con.prototype // or let obj = null obj = Object.create(con.prototype) let result = con.apply(obj, args) let flag = result && (typeof result === 'object' || typeof result === 'function') return flag ? result : obj}call & applyFunction.prototype.myCall = function (context, ...args) { context = context === null || context === undefined ? globalThis : Object(context) const fnk = Symbol() Object.defineProperty(context, fnk, { enumerable: false, value: this }) const r = context[fnk](...args) delete context[fnk] return r}Function.prototype.myApply = function (context, args) { context = context === null || context === undefined ? globalThis : Object(context) const fnk = Symbol() Object.defineProperty(context, fnk, { enumerable: false, value: this }) const r = context[fnk](...args) delete context[fnk] return r}bindFunction.prototype.myBind = function (context, ...args) { context = context === null || context === undefined ? globalThis : Object(context) const func = this return function (...subArgs) { const allArgs = [...args, ...subArgs] if (new.target) { return new func(...allArgs) } else { return func.apply(context, allArgs) } }}createfunction myCreate(obj) { function F() { } F.prototype = obj return new F()}instanceoffunction myInstanceof(obj, con) { let pro = obj?.__proto__ while (pro) { if (pro === con.prototype) { return true } pro = pro.__proto__ } return false}Promise.allPromise.myAll = function (promiseArr) { return new Promise((resolve, reject) => { if (!Array.isArray(promiseArr)) { throw new Error('argument should be array') } let count = 0, promiseArrLength = promiseArr.length, resolved = [] for (let i = 0; i < promiseArr.length; ++i) { Promise.resolve(promiseArr[i]).then((res) => { resolved[i] = res ++count if (count === promiseArrLength) { resolve(resolved) } }, (rej) => { reject(rej) }) } })}Promise.racePromise.myRace = function (promiseArr) { return new Promise((resolve, reject) => { if (!Array.isArray(promiseArr)) { throw new Error('argument should be array'); } promiseArr.forEach(promise => { Promise.resolve(promise).then(resolve, reject) }) })}Promise.retryPromise.retry = function (fn, retries) { return new Promise((resolve, reject) => { Promise.resolve(fn()) .then(resolve) .catch(error => { if (retries > 0) { console.log(`Retrying... ${retries} attempts left`); Promise.retry(fn, retries - 1) .then(resolve) .catch(reject); } else { reject(error); } }); })}Promise.queuePromise.queue = function (promiseArr) { return new Promise((resolve, reject) => { const resultArr = []; // (async () => { // for (let i = 0; i < promiseArr.length; i++) { // try { // resultArr[i] = await promiseArr[i]() // } catch (err) { // reject(err) // return // } // } // resolve(resultArr) // })() // or function runPromise(index) { if (index >= promiseArr.length) { resolve(resultArr) return } promiseArr[index]().then(res => { resultArr[index] = res runPromise(index + 1) }, err => { reject(err) return }) } runPromise(0) })}promisifyfunction promisify(func) { return function (...args) { return new Promise((resolve, reject) => { func(...args, (err, result) => { if (err) { reject(err); } else { resolve(result); } }); }); };}Event Busclass EventBus { constructor() { this.events = {} } on(event, callback) { if (this.events[event] === undefined) { this.events[event] = new Set() } this.events[event].add(callback) } off(event, callback) { if (this.events[event]) { this.events[event].delete(callback) if (!this.events[event].size) delete this.events[event] } } emit(event, ...args) { if (this.events[event]) { this.events[event].forEach(cb => { cb(...args) }) } } once(event, callback) { const onceCallback = (...args) => { this.off(event, onceCallback) callback(...args) } this.on(event, onceCallback) }}sumOffunction sum(...args1) { const ret = (...args2) => sum(...args1, ...args2) ret.sumOf = () => args1.reduce((acc, cur) => acc + cur, 0); return ret}console.log(sum(1, 2, 3).sumOf()); // 6console.log(sum(1, 2)(3).sumOf()); // 6console.log(sum(1)(2)(3).sumOf()); // 6hardManhardMan('潘潘')Hi! I am 潘潘.hardMan('潘潘').study('敲码')Hi! I am 潘潘.I am studying 敲码.hardMan('潘潘').rest(3).study('敲码')Hi! I am 潘潘.【等待三秒】I am studying 敲码.hardMan('潘潘').restFirst(3).study('敲码')【等待三秒】Hi! I am 潘潘.I am studying 敲码.function hardMan(name) { this.name = name; this.queue = [] this.queue.push(() => { return new Promise(resolve => { console.log(`Hi! I am ${this.name}`) resolve() }) }) this.rest = (sec) => { this.queue.push(() => new Promise(resolve => { setTimeout(resolve, sec * 1000) })) return this } this.restFirst = (sec) => { this.queue.unshift(() => new Promise(resolve => { setTimeout(resolve, sec * 1000) })) return this } this.study = (name) => { this.queue.push(() => new Promise(resolve => { console.log(`I am studying ${name}`) resolve() })) return this } this._run = () => { if (this.queue.length) { const task = this.queue.shift() task().then(() => { this._run() }) } } setTimeout(this._run) return this}限制最大并发class Schedular { constructor() { this.tasks = []; this.runningTasks = 0; this.maxRunningTasks = 2; } add(task) { return new Promise((resolve) => { this.tasks.push([task, resolve]); this._run(); }); } _run() { if (this.runningTasks >= this.maxRunningTasks || !this.tasks.length) return this.runningTasks++; const [task, resolve] = this.tasks.shift(); task().then(() => { this.runningTasks--; resolve(); this._run(); }) }}const timeout = (ms) => new Promise((resolve) => setTimeout(resolve, ms));const scheduler = new Schedular();const addTask = (time, order) => { scheduler.add(() => timeout(time)).then(() => console.log(order))}addTask(1000, 1);addTask(1000, 2);// 1秒后输出12addTask(1000, 3);addTask(1000, 4);// 再1秒后输出34带优先级的fetchclass MyFetch { constructor() { this.queue = [] this.count = 0 this.maxCount = 5 } fetch(url, options = {}, priority = 0) { return new Promise((resolve, reject) => { this.queue.push([priority, url, options, resolve, reject]) this._run() }) } _run() { if (this.count >= this.maxCount || this.queue.length === 0) return this.count++ this.queue.sort((a, b) => b[0] - a[0]) const [_, url, options, resolve, reject] = this.queue.shift() fetch(url, options).then(resolve).catch(reject).finally(() => { this.count-- this._run() }) }}重试函数 runWithRetry重试函数:实现一个重试函数runWithRetry(handler: Function, times: number = 1, timeout?: number),支持异步function runWithRetry(handler, times = 1, timeout) { return new Promise((resolve, reject) => { let timer; let timeouted = false; if (timeout !== undefined) { timer = setTimeout(() => { reject(); timeouted = true; }, timeout); } function retry(times) { if (times <= 0) { clearTimeout(timer); reject(); return; } Promise.resolve(handler()).then( (res) => { clearTimeout(timer); resolve(res); }, () => { if (!timeouted) retry(times - 1); }, ); } retry(times); });}