记常遇到的JS手写
张渊 Lv2

面试中遇到的一些JS手写题以及一些常见的JS手写题记录。

手写 call、apply 和 bind

call、apply和bind三者的用法和区别

call、apply和bind这三个函数的第一个参数都是this指向函数,第二个参数有差别

  • call后面的参数都是直接以逗号分隔开依次写在后面传递的;
  • apply的后一个参数是以数组形式传递的,有多个参数都需要写在数组中进行传递;
  • bind的除了返回是函数之外,他的参数和call一样;

三者参数不限定是string类型,允许是各种类型,包括函数、object等。

手写 call

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
// 原型myCall方法
Function.prototype.myCall = function (context) {
console.log(this); // getName函数
console.log(context); // 传递的参数person1
if (typeof this !== 'function') {
throw new Error('error');
}
context = context || window;
// 考虑参数
// 拿到除了第一个参数之外的参数
var args = [...arguments].slice(1);
context.fn = this; // 改变this指向
var result = context.fn(...args);
return result;
}

// 测试
var person = {
name: '执笔写下信念',
getName: function () {
return this.name
}
};
var person1 = {
name: '小手冰凉'
};
console.log(person.getName.myCall(person1, 1, 2, 3));

手写 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
Function.prototype.myApply = function (context) {
console.log(context); // 参数
console.log(this); // getName函数
context = context || window;
context.fn = this;
let result;
if (arguments[1]) {
result = context.fn([...arguments[1]]);
} else {
result = context.fn();
}
delete context.fn;
return result;
}

// 测试
const person = {
name = "小手冰凉",
getName: function () {
console.log(arguments)
console.log(this.name);
}
};
const people = {
name: "纸币写下信念"
};
console.log(person.getName.myApply(people, [1, 2, 3]));

手写 bind

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
Function.prototype.myBind = function (context) {
const self = this;
if (typeof this !== 'function') {
throw new Error('error');
}
context = context || window;
// 获取除第一个参数外的所有参数
const args = [...arguments].slice(1);
context.fn = self;
return function () {
// 参数合并
const allArg = [...args, ...arguments];
const result = context.fn(...allArg);
return result;
}
}

// 测试
var person = {
name: '纸币写下信念',
getName: function () {
return this.name
}
};
var person1 = {
name: '小手冰凉'
};
const p1 = person.getName.myBind(person1, 10, 20, 30);
console.log(p1(40, 50));

手写 flat

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
// 实现一个flat
Array.prototype.myFlat = function (n) {
// 不传n默认为1
if (!n) {
n = 1
}
const self = this;
const fn = (arr, n) => {
let res = [];
if (n === 0) {
return arr;
}
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
res = res.concat(fn(arr[i], n - 1));
} else {
res = res.concat(arr[i]);
}
}
return res;
}
return fn(self, n);
}

// reduce方式
function myFlat(arr, n) {
if (n === 0) {
return arr;
}
return arr.reduce((res, value) => {
if (Array.isArray(value)) {
res = res.concat(myFlat(value, n - 1));
} else {
res = res.concat(value);
}
return res;
}, [])
}

const arr = [1, 2, [1, 3, [54, 3, 4]], [22, 546, [123, 435, 123], 3]];
console.log(arr.myFlat(2));

函数柯里化

柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function curry(fn, ...args) {
const { length } = fn;
if (length <= args.length) {
return fn(...args);
} else {
return curry.bind(null, fn, ...args)
}
}

// 普通函数
function fn(a, b, c) {
console.log(a, b, c);
}
// 柯里化函数
const _fn = curry(fn);
// 测试
fn(1, 2, 3); // 1 2 3
_fn(1)(2)(3); // 1 2 3

手写 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
// 基础架构
function MyPromise(excutor) {
let self = this;
self.status = 'pending';
self.value = null;
self.reason = null;
// 发布订阅模式、支持异步
self.onFulfilledCallback = [];
self.onRejectedCallback = [];

// 成功回调
function resolve(value) {
// 状态判断
if (self.status === 'pending') {
self.value = value;
self.status = 'fulfilled';
// 状态改变就 执行 => 发布
self.onFulfilledCallback.forEach(item => item(value));
}
}

// 失败回调
function reject(reason) {
if (self.status === 'pending') {
self.reason = reason;
self.status = 'rejected';
// 状态改变就 执行 => 发布
self.onFulfilledCallback.forEach(item => item(reason));
}
}

// 添加立即执行器
try {
excutor(resolve, reject);
} catch (error) {
reject(error);
}
}

// 2. then方法,接收一个成功的回调和一个失败的回调
MyPromise.prototype.then = function (onFulfilled, onRejected) {
// 6. 状态更改了就会调用.then()
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (data) { return data };
onRejected = typeof onRejected === 'function' ? onRejected : function (err) { throw err };
let self = this;
// 8. 订阅操作
// if (self.status === 'pending') {
// self.onFulfilledCallback.push(onFulfilled);
// self.onRejectedCallback.push(onRejected);
// }
if (self.status === 'pending') {
return new MyPromise((resolve, reject) => {
self.onFulfilledCallback.push(() => {
let x = onFulfilled(self.value);
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x);
});
self.onRejectedCallback.push(() => {
let x = onFulfilled(self.value);
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x);
});
});
}
if (self.status === 'fulfilled') {
return new MyPromise((resolve, reject) => {
try {
let x = onFulfilled(self.value);
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x);
} catch (error) {
reject(error);
}
});
}
if (self.status === 'rejected') {
return new MyPromise((resolve, reject) => {
try {
let x = onRejected(self.value);
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x);
} catch (error) {
reject(error);
}
});
}
}

MyPromise.prototype.catch = function (fn) {
return this.then(null, fn);
}

// MyPromise.resolve()方法
MyPromise.resolve = function (value) {
return value instanceof MyPromise ? value : new MyPromise(resolve => resolve(value));
}

// MyPromise.reject()方法
MyPromise.reject = function (error) {
return new MyPromise(reject => reject(error));
}

// MyPromise.all()方法
MyPromise.all = function(promiseArr) {
let count = 0;
let result = [];
return new MyPromise((resolve, reject) => {
// promiseArr为0直接返回result
if(!promiseArr.length){
resolve(result);
}
promiseArr.forEach((p, idx) => {
MyPromise.resolve(p).then(value => {
count++;
result[idx] = value;
if(count === promiseArr.length){
resolve(result);
}
}).catch(error => {
reject(error);
})
})
})
}

// MyPromise.race()方法
MyPromise.race = function(promiseArr) {
return new MyPromise((resolve, reject) => {
promiseArr.forEach(p => {
MyPromise.resolve(p).then(value => {
resolve(value);
}).catch(error => {
reject(error);
})
})
})
}

// 测试
let demo = new MyPromise((resolve, reject) => {
console.log('MyPromise 已经完成');
setTimeout(() => {
resolve(1);
}, 1000)
});
demo.then((data) => {
console.log(data);
});
 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务
总字数 12.9k 访客数 访问量