web前端手撕合集
这篇文章包含了一些前端常见的手撕题,和部分我在面试过程中遇到的手撕题。非算法题。
防抖
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
}
数组flat
Array.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
}, [])
}
数组reduce
Array.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
}
new
function 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 & apply
Function.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
}
bind
Function.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)
}
}
}
create
function myCreate(obj) {
function F() { }
F.prototype = obj
return new F()
}
instanceof
function myInstanceof(obj, con) {
let pro = obj?.__proto__
while (pro) {
if (pro === con.prototype) {
return true
}
pro = pro.__proto__
}
return false
}
Promise.all
Promise.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.race
Promise.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.retry
Promise.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.queue
Promise.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)
})
}
promisify
function promisify(func) {
return function (...args) {
return new Promise((resolve, reject) => {
func(...args, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
};
}
Event Bus
class 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)
}
}
sumOf
function 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()); // 6
console.log(sum(1, 2)(3).sumOf()); // 6
console.log(sum(1)(2)(3).sumOf()); // 6
hardMan
hardMan('潘潘')
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秒后输出12
addTask(1000, 3);
addTask(1000, 4);
// 再1秒后输出34
带优先级的fetch
class 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);
});
}
#前端##面经#