各类手写问题汇总
Youky ... 2021-10-11 About 4 min
# 更改函数 this 指向的三种方法
# call
函数首先接收上下文参数 context,若没有则默认为 window
在 context 上绑定要调用的函数 func(也就是 this 的指向)
截取 arguments 得到调用函数时要传入的参数
调用后,删除 context 的 func 属性
Function.prototype.myCall = function (context, ...args) { // 调用者不是函数,报错 if (typeof this !== "function") { throw new TypeError("调用者不是函数") } // 若没有传入context,默认值为widow context = context || window // 在context上下文中调用函数 context.func = this let res = context.func(...args) // 调用后删除func属性 delete context.func // 返回调用结果 return res }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# apply
Function.prototype.myApply = function (context, args) {
// 调用者必须是函数
if (typeof this !== "function") {
throw new TypeError("调用者不是函数")
}
// apply方法的第二个参数如果存在,则必须是数组
if (args && !Array.isArray(args)) {
throw new TypeError("第二个参数必须是数组")
}
context = context || window
context.fn = this
const res = args ? context.fn(...args) : context.fn()
delete context.fn
return res
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# bind
Function.prototype.myBind = function (context, ...args) {
if (typeof this !== "function") {
throw new TypeError("调用者不是函数")
}
const self = this
return function f() {
// 说明f是在当做构造函数调用
// 参考new一个对象的过程,用new调用时这里的this的__proto__指向f
if (this instanceof f) {
return new self(...args, ...arguments)
}
// 当做普通函数调用
else {
return self.apply(context, [...args, ...arguments])
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 深拷贝函数
# 最简单的实现
JSON.parse(JSON.stringify())
1
问题:
- 不能拷贝函数
- 不能解决循环引用问题
- 无法拷贝一些特殊对象,如 Map、Set 等
# 完整实现
/**
* 深拷贝函数
* @param {any} data 要克隆的变量
* @param {WeakMap} map 记录已克隆的变量
* @returns
*/
function clone(data, map = new WeakMap()) {
// 非引用类型变量(或函数),直接返回
if (typeof data !== "object" || data == null) return data
// map中已存在的变量,直接返回
if (map.has(data)) {
return map.get(data)
}
// 判断数据的真实类型
const type = Object.prototype.toString.call(data).slice(8, -1)
// 针对不同类型递归处理
if (type == "Date") return new Date(data)
else if (type == "RegExp") return new RegExp(data)
else if (type == "Object" || type == "Array") {
// 如果是对象或数组
const res = type == "Array" ? [] : {}
map.set(data, res)
for (let key in data) {
res[key] = clone(data[key], map)
}
return res
} else if (type == "Map") {
// 如果是Map
const res = new Map()
map.set(data, res)
for (let key of data.keys()) {
res.set(key, clone(data.get(key), map))
}
return res
} else if (type == "Set") {
// 如果是Set
const res = new Set()
map.set(data, res)
for (let value of data) {
res.add(clone(value, map))
}
return res
}
}
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
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
# 实现一个 new
function myNew(con, ...args) {
// 如果new后面不是函数类型,则报错
if (typeof con !== "function") {
throw new TypeError(`${con} is not a constructor`)
}
// 创建对象,并调用构造函数
const obj = Object.create(con.prototype)
const res = con.apply(obj, args)
// 若构造函数返回了引用类型变量,则返回它;否则返回obj
return res instanceof Object ? res : obj
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# Promise
# 实现一个 Promise
# 简易实现(不能链式调用)
class MyPromise {
constructor(fn) {
this.status = "pending"
this.value = null
this.reason = null
this.onFulfiledList = []
this.onRejectedList = []
const resolve = (data) => {
if (this.status == "pending") {
this.status = "fulfilled"
this.value = data
setTimeout(() => {
this.onFulfiledList.forEach((f) => f(data))
})
}
}
const rejected = (data) => {
if (this.status == "pending") {
this.status = "rejected"
this.reason = data
setTimeout(() => {
this.onRejectedList.forEach((f) => f(data))
})
}
}
try {
fn(resolve, rejected)
} catch (e) {
rejected(e)
}
}
then(resolveFn, rejectedFn) {
const realResolveFn =
typeof resolveFn == "function"
? resolveFn
: (data) => Promise.resolve(data)
const realRejectedFn =
typeof rejectedFn == "function"
? rejectedFn
: (err) => Promise.reject(err)
if (this.status == "pending") {
this.onFulfiledList.push(realResolveFn)
this.onRejectedList.push(realRejectedFn)
} else if (this.status == "fulfilled") {
realResolveFn(this.value)
} else {
realRejectedFn(this.reason)
}
return this
}
}
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
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
# 实现链式调用
主要是修改了 then 方法
class MyPromise {
constructor(fn) {
this.status = "pending"
this.value = null
this.reason = null
this.onFulfiledList = []
this.onRejectedList = []
const resolve = (data) => {
if (this.status == "pending") {
this.status = "fulfilled"
this.value = data
setTimeout(() => {
this.onFulfiledList.forEach((f) => f(data))
})
}
}
const rejected = (data) => {
if (this.status == "pending") {
this.status = "rejected"
this.reason = data
setTimeout(() => {
this.onRejectedList.forEach((f) => f(data))
})
}
}
try {
fn(resolve, rejected)
} catch (e) {
rejected(e)
}
}
then(resolveFn, rejectedFn) {
const realResolveFn =
typeof resolveFn == "function"
? resolveFn
: (data) => Promise.resolve(data)
const realRejectedFn =
typeof rejectedFn == "function"
? rejectedFn
: (err) => Promise.reject(err)
// 为了实现链式调用,then方法应返回一个Promise
// 无论当前Promise的状态,都直接向回调队列添加回调函数。
// 因为回调的执行是异步事件,而回调的添加是同步执行的
return new MyPromise((resolve, rejected) => {
// 这里的this指向的是当前执行的Promise而不是新返回的Promise(所以这里是箭头函数)
this.onFulfiledList.push(() => {
try {
// 前一个回调函数的返回结果
const x = realResolveFn(this.value)
// 如果在前一个回调函数中返回了Promise,
// 则传给下一个回调的是这个Promise的value,而不是Promise本身
x instanceof MyPromise ? x.then(resolve, rejected) : resolve(x)
} catch (err) {
rejected(err)
}
})
this.onRejectedList.push(() => {
try {
const x = realRejectedFn(this.value)
x instanceof MyPromise ? x.then(resolve, rejected) : resolve(x)
} catch (e) {
rejected(e)
}
})
})
}
}
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
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
# 实现 Promise.all
Promise.all = (iterable) => {
return new Promise((resolve, reject) => {
const arr = Array.from(iterable)
if (arr.length == 0) {
return reject("arguments must be an array")
}
let resolvedNum = 0
const resolvedValues = []
for (let i = 0; i < arr.length; i++) {
Promise.resolve(arr[i])
.then((value) => {
resolvedNum++
resolvedValues[i] = value
if (resolvedNum == arr.length) {
return resolve(resolvedValues)
}
})
.catch((e) => {
return reject(e)
})
}
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 实现 async/await 函数
实现需求:
- 按 generator 的写法书写异步代码,实现和 async/await 相同的效果
- 接收 generator 函数为参数
function genToAsync(gen) {
return function () {
return new Promise((resolve, rejected) => {
const ito = gen()
function walk(arg) {
try {
const { value, done } = ito.next(arg)
if (done) {
// done为true时,value是函数的返回值,此时是否是Promise不影响
resolve(value)
} else {
// 兼容yield后不是Promise的情况
Promise.resolve(value)
.then((res) => walk(res))
.catch((err) => rejected(err))
}
} catch (e) {
rejected(e)
}
}
walk()
})
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
使用测试:
// 模拟异步请求函数
function getData() {
return new Promise((r) => {
setTimeout(() => {
r(`data`)
}, 500)
})
}
// 定义generator函数
function* testG() {
// await被编译成了yield
const data = yield getData()
console.log("data: ", data)
const data2 = yield getData()
console.log("data2: ", data2)
const data3 = yield 3
console.log(`data3: ${data3}`)
return "success"
}
// 测试
const asyncFunction = genToAsync(testG)
asyncFunction().then((res) => console.log(`finish, return: ${res}`))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24