js中面试常用

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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/*
https://github.com/YvetteLau/Blog/issues/2
1. new Promise(fn: (resolve, reject) => {}): Promise
2. Promise.then(handleFulfilled, handleRejected): Promise
=> then方法会根据Promise实例中fn函数是resolve还是reject被调用,然后决定执行handleFulfilled哈市handleRejected
3. Promise.catch(handleRejected): Promise
=> 捕获链式调用中没有被处理的错误(reject,Error等)
4. promise.finally(onfinally: () => void)
=> 链式调用最后执行函数
5. promise.all(iterable)
=> 这个方法返回一个新的promise对象,该promise对象在iterable参数对象里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败。这个新的promise对象在触发成功状态以后,会把一个包含iterable里所有promise返回值的数组作为成功回调的返回值,顺序跟iterable的顺序保持一致;如果这个新的promise对象触发了失败状态,它会把iterable里第一个触发失败的promise对象的错误信息作为它的失败错误信息。
6. Promise.any(iterable)
=> 接收一个Promise对象的集合,当其中的一个 promise 成功,就返回那个成功的promise的值。
7. Promise.resolve(value)
8. Promise.reject(value)
9. Promise.race(iterable)
=> Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
*/

class MyPromise {
constructor(fn) {
// 状态为pending, fulfill, reject
this.state = 'pending'
// then注册的成功函数
this.successFun = []
// then注册的失败函数
this.failureFun = []

const resolve = val => {
if (this.state !== 'pending') return
this.state = 'fulfilled'
setTimeout(() => {
log('run')
// 给当前的promise的resolve里面的val值传递给then
this.successFun.forEach(item => item.call(this, val))
})
}

const reject = (err) => {
if (this.state !== 'pending') return
this.state = 'rejected'
setTimeout(() => {
this.failureFun.forEach(item => item.call(this, err))
})
}

try {
fn(resolve, reject)
} catch (error) {
reject(error)
}
}
then(handleResolved, handleRejected) {
handleResolved = typeof handleResolved !== 'function' ? (v) => v : handleResolved
handleRejected = typeof handleRejected !== 'function' ? (err) => {
throw err
} : handleRejected
log('handleResolved', handleResolved.toString())
log('handleRejected', handleRejected.toString())
return new MyPromise((resolve, reject) => {
// 这里的this指向的上一级作用域
// 这里需要包装一层
// 因为handleResolved可能执行出错或者返回一个MyPromise
this.successFun.push(val => {
try {
let x = handleResolved(val)
// 这里resolve(x),把x传入下一个then
// x.then(resolve, reject), 这里通过
// resolve本身的promise拿到外面promise在
// resolve传的值
// resolve其实也就是给then传值
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
} catch (error) {
reject(error)
}
})

this.failureFun.push(val => {
try {
let x = handleRejected(val)
x instanceof MyPromise ? x.then(resolve, reject) : reject(x)
} catch (error) {
reject(error)
}
})
})
}
/**
如果 value 是个 thenable 对象,返回的promise会“跟随”这个thenable的对象,采用它的最终状态
如果传入的value本身就是promise对象,那么Promise.resolve将不做任何修改、原封不动地返回这个promise对象。
其他情况,直接返回以该值为成功状态的promise对象。
*/
static resolve(params) {
if (params instanceof MyPromise) {
return params
}
return new MyPromise((resolve, reject) => {
if (params && params.then && typeof params.then === 'function') {
params.then(resolve, reject)
} else {
resolve(params)
}
})
}
// Promise.reject方法和Promise.resolve不同,Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason)
})
}
// 用于指定出错时的回调,是特殊的then方法,catch之后,可以继续 .then
catch(handleRejected) {
return this.then(null, handleRejected)
}
// 不管成功还是失败,都会走到finally中,并且finally之后,还可以继续then。并且会将值原封不动的传递给后面的then.
finally(callback) {
return this.then(
value => {
return MyPromise.resolve(callback()).then(() => {
return value
})
},
err => {
return MyPromise.resolve(callback()).then(() => {
throw err
})
}
)
}
static all(promiseArr) {
const result = []
let count = 0
return new MyPromise((resolve, reject) => {
for (let i = 0; i < promiseArr.length; i++) {
MyPromise.resolve(promiseArr[i]).then(
res => {
result[i] = res
count++
if (count === promiseArr.length) {
resolve(result)
}
}),
err => {
reject(err)
}
}
})
}
race(promiseArr) {
return new MyPromise((resolve, reject) => {
for (let i = 0; i < promiseArr.length; i++) {
MyPromise.resolve(promiseArr[i]).then(
(res) => {
resolve(res);
},
(err) => {
reject(err);
}
);
}
});
}
}

节流和防抖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const debounce = (fn, delay, immediate) => {
let later = null
return (...args) => {
if (!later && immediate) {
fn(...args)
}
later && clearTimeout(later)
later = setTimeout(func, delay, ...args)
}
}

// 节流,技能有cd
const throttle = (func, delay) => {
let later = null
return (...args) => {
if (later) return
later = setTimeout(() => {
func(...args)
later = null
}, delay)
}
}

柯里化

1
2
3
4
5
6
const curring = (func) => (...args) => {
if (func.length <= args.length) {
return func(...args)
}
return (...args2) => curring(func)(...args, ...args2)
}

instanceOf

1
2
3
4
5
6
7
8
9
10
11
const myInstanceof = (left, right) => {
while (true) {
if (left === null) {
return false
}
if (left.__proto__ === right.prototype) {
return true
}
left = left.__proto__
}
}

deepClone

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const isObject = (val) => {
return typeof val === 'object' && val !== null
}

const deepClone = (obj, hash = new WeakMap()) => {
if (!isObject(obj)) return obj
if (hash.has(obj)) {
return hash.get(obj)
}
let target = Array.isArray(obj) ? [] : {}
hash.set(obj, target)
Reflect.ownKeys(obj).forEach(key => {
if (isObject(obj[key])) {
target[key] = deepClone(obj[key], hash)
} else {
target[key] = obj[key]
}
})
return target
}

call, bind, apply

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
Function.prototype.MyCall = function (context, ...args) {
context = context || window
let fn = Symbol()
context[fn] = this
const result = context[fn](...args)
delete context.fn
return result
}

Function.prototype.myApply = function (context, args) {
context = context || window
let fn = Symbol()
context[fn] = this
const result = context.func(...args)
delete context.fn
return result
}

Function.prototype.Mybind = function (context, ...args) {
context = context || window
context.func = this
const result = () => {
const res = context.func(...args)
delete context.func
return res
}
return result
}

new方法

1
2
3
4
5
6
7
8
const New = (func, ...args) => {
let obj = Object.create(func.prototype)
let result = func.call(obj, ...args)
if (typeof result === 'object') {
return result
}
return obj
}

EventEmitter实现

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
class EventEmitter {
constructor() {
// 订阅总集合
this.events = {}
}
/* 添加关于event的订阅者 */
on(event, callback) {
if (this.events[event]) {
this.events[event].push(callback);
} else {
this.events[event] = [callback]
}
}
/* 触发-发布 */
emit(event) {
if (this.events[event]) {
this.events[event].forEach((fn) => fn())
}
}

// 删除订阅了某个event的订阅者
off(event, callback) {
if (this.events[event]) {
this.events[event] = this.events[event].filter(fn => fn !== callback)
}
}

// 订阅某event,执行一次后就不再订阅了
once(event, callback) {
let that = this;
function cancel() {
callback()
that.off(event, cancel);
}
this.on(event, cancel)
}
}

setInterval优化

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
/**
* 关于setInterval:
* 再次强调,定时器指定的时间间隔,表示的是何时将定时器的代码添加到消息队列,而不是何时执行代码。
* 有两个缺点:
* 1. 使用 setInterval 时,某些间隔会被跳过;
* 2. 可能多个定时器会连续执行;
* 每个 setTimeout 产生的任务会直接 push 到任务队列中;
而 setInterval 在每次把任务 push 到任务队列前,
都要进行一下判断(看上次的任务是否仍在队列中,如果有则不添加,没有则添加)。
*
* @param {*} fn
* @param {*} timer
* @returns
*/
function interval(fn, timer) {
let later = null
function loop() {
fn()
// 在前一个定时器执行完前,不会向队列插入新的定时器
// 保证定时器间隔(解决缺点二)
later = setTimeout(loop, timer)
}
loop()
return {
cancel: () => {
clearTimeout(later)
}
}
}

compose

1
2
3
4
5
6
7
const compose = (...fns) => {
if (!fns.length) return null
if (fns.length === 1) return fns[0]
return fns.reduce((cur, next) => {
return (...args) => next(cur(...args))
})
}
文章作者: woyao
文章链接: https://chenwoyao.github.io/2021/04/23/前端笔记/javascript系列/js中面试常用/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 woyao的博客