前端手写题 15 题速记版

前端手写题 15 题速记版

1. 手写 Promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
class MyPromise {
constructor(executor) {
this.state = 'pending'
this.value = undefined
this.reason = undefined
this.onFulfilledCallbacks = []
this.onRejectedCallbacks = []

const resolve = (value) => {
if (this.state !== 'pending') return
this.state = 'fulfilled'
this.value = value
this.onFulfilledCallbacks.forEach(fn => fn())
}

const reject = (reason) => {
if (this.state !== 'pending') return
this.state = 'rejected'
this.reason = reason
this.onRejectedCallbacks.forEach(fn => fn())
}

try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}

then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }

const promise2 = new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
queueMicrotask(() => {
try {
const x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}

if (this.state === 'rejected') {
queueMicrotask(() => {
try {
const x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}

if (this.state === 'pending') {
this.onFulfilledCallbacks.push(() => {
queueMicrotask(() => {
try {
const x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
})

this.onRejectedCallbacks.push(() => {
queueMicrotask(() => {
try {
const x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
})
}
})

return promise2
}

catch(onRejected) {
return this.then(null, onRejected)
}

static resolve(value) {
return new MyPromise(resolve => resolve(value))
}

static reject(reason) {
return new MyPromise((_, reject) => reject(reason))
}
}

function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected'))
}

if (x instanceof MyPromise) {
x.then(resolve, reject)
} else {
resolve(x)
}
}

2. 手写深拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
function deepClone(obj, map = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj
if (map.has(obj)) return map.get(obj)

const clone = Array.isArray(obj) ? [] : {}
map.set(obj, clone)

Reflect.ownKeys(obj).forEach(key => {
clone[key] = deepClone(obj[key], map)
})

return clone
}

3. 手写防抖

1
2
3
4
5
6
7
8
9
function debounce(fn, delay) {
let timer = null
return function (...args) {
clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}

4. 手写节流

1
2
3
4
5
6
7
8
9
10
function throttle(fn, delay) {
let lastTime = 0
return function (...args) {
const now = Date.now()
if (now - lastTime >= delay) {
lastTime = now
fn.apply(this, args)
}
}
}

5. 手写 call

1
2
3
4
5
6
7
8
Function.prototype.myCall = function (context, ...args) {
context = context == null ? globalThis : Object(context)
const key = Symbol('fn')
context[key] = this
const result = context[key](...args)
delete context[key]
return result
}

6. 手写 apply

1
2
3
4
5
6
7
8
Function.prototype.myApply = function (context, args = []) {
context = context == null ? globalThis : Object(context)
const key = Symbol('fn')
context[key] = this
const result = context[key](...args)
delete context[key]
return result
}

7. 手写 bind

1
2
3
4
5
6
7
8
9
Function.prototype.myBind = function (context, ...args1) {
const fn = this
function boundFn(...args2) {
const isNew = this instanceof boundFn
return fn.apply(isNew ? this : context, [...args1, ...args2])
}
boundFn.prototype = Object.create(fn.prototype)
return boundFn
}

8. 手写 new

1
2
3
4
5
function myNew(fn, ...args) {
const obj = Object.create(fn.prototype)
const result = fn.apply(obj, args)
return (result !== null && (typeof result === 'object' || typeof result === 'function')) ? result : obj
}

9. 手写 instanceof

1
2
3
4
5
6
7
8
9
function myInstanceof(left, right) {
let proto = Object.getPrototypeOf(left)
const prototype = right.prototype
while (proto) {
if (proto === prototype) return true
proto = Object.getPrototypeOf(proto)
}
return false
}

10. 手写数组扁平化

1
2
3
4
5
function flatten(arr) {
return arr.reduce((prev, cur) => {
return prev.concat(Array.isArray(cur) ? flatten(cur) : cur)
}, [])
}

11. 手写柯里化

1
2
3
4
5
6
7
8
9
function curry(fn, ...args) {
return function (...rest) {
const allArgs = [...args, ...rest]
if (allArgs.length >= fn.length) {
return fn(...allArgs)
}
return curry(fn, ...allArgs)
}
}

12. 手写 Promise.all

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function promiseAll(promises) {
return new Promise((resolve, reject) => {
const result = []
let count = 0

if (promises.length === 0) {
resolve([])
return
}

promises.forEach((item, index) => {
Promise.resolve(item).then(value => {
result[index] = value
count++
if (count === promises.length) {
resolve(result)
}
}, reject)
})
})
}

13. 手写 Promise.race

1
2
3
4
5
6
7
function promiseRace(promises) {
return new Promise((resolve, reject) => {
promises.forEach(item => {
Promise.resolve(item).then(resolve, reject)
})
})
}

14. 手写发布订阅

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class EventEmitter {
constructor() {
this.events = {}
}

on(eventName, callback) {
if (!this.events[eventName]) {
this.events[eventName] = []
}
this.events[eventName].push(callback)
}

emit(eventName, ...args) {
if (!this.events[eventName]) return
this.events[eventName].forEach(fn => fn(...args))
}

off(eventName, callback) {
if (!this.events[eventName]) return
this.events[eventName] = this.events[eventName].filter(fn => fn !== callback)
}

once(eventName, callback) {
const wrapper = (...args) => {
callback(...args)
this.off(eventName, wrapper)
}
this.on(eventName, wrapper)
}
}

15. 手写 Proxy 响应式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver)
console.log('更新了', key, value)
return result
},
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key)
console.log('删除了', key)
return result
}
})
}

高频补充

  • Promise:核心是状态、then 链式调用、异常捕获
  • 深拷贝:循环引用必考,WeakMap 加分
  • 防抖节流:区别 + 场景必问
  • call/apply/bind:核心都是显式绑定 this
  • new:核心是原型链接 + 返回对象规则
  • instanceof:顺着原型链找
  • Proxy:Vue3 响应式基础