前言
最近整理了一些前端方向常见的手写代码题,动手实现一遍之后,对一些常见的API或者组件原理有了更多的理解,故在此做一下记录和分享😁😁
基础
请求相关
原生ajax实现
javascript1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19const Ajax = {
// data应为'a=a1&b=b1'这种字符串格式,在jq里如果data为对象会自动将对象转成这种字符串格式
post: function (url, data, callback) {
var xhr = new XMLHttpRequest();
xhr.open('POST', url, false);
// 添加http头,发送信息至服务器时内容编码类型
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
callback(JSON.parse(xhr.responseText));
}
}
xhr.send(JSON.stringify(data));
}
}
Ajax.post('/data', { name: 'xiaoming' }, data => {
console.log('data: ', data)
})websocket
javascript1
2
3
4
5
6
7
8
9
10
11
12
13
14var ws = new WebSocket("ws://localhost:8080");
ws.onopen = function () {
console.log("open");
ws.send("hello"); //将消息发送到服务端
}
ws.onmessage = function (e) {
console.log(e.data);
}
ws.onclose = function (e) {
console.log("close");
}
ws.onerror = function (e) {
console.log(error);
}fetch
javascript1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18fetch('/data', {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({ name: 'xiaoming' }),
credentials: 'same-origin'
})
.then(response => {
if (response.ok) {
return response.json()
}
return Promise.reject('server error: ' + response.status)
})
.then(data => {
console.log(data)
})
.catch(error => console.error('catch error:', error))jsonp的实现
javascript1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21let jsonpFunc = () => {}
function jsonp(url, delay) {
return new Promise((resolve, reject) => {
jsonpFunc = resolve
const body = document.getElementsByTagName('body')[0]
const script = document.createElement('script')
script.src = url
body.appendChild(script)
setTimeout(() => {
reject('timeout')
}, delay)
})
}
jsonp('./js/index.js?name=jsonpFunc', 1000)
.then(res => {
console.log(res)
}, err => {
console.log(err)
})
Promise
Promise的简单实现
javascript1
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// todo: error的向后传递特性
class _Promise {
constructor(fn) {
/**
* state promise状态
* onFullFilled 兑现之后的回调函数
* onRejected 拒绝之后的回调函数
* onCatch catch函数
*/
this.state = 'pending'
this.onFullFilled = null
this.onRejected = null
this.onCatch = null
this.nextResolve = null
this.nextReject = null
fn(this.resolve, this.reject)
}
then = (onFullFilled, onRejected) => {
this.onFullFilled = onFullFilled
this.onRejected = onRejected
// 链式调用
return new _Promise((resolve, reject) => {
this.nextResolve = resolve
this.nextReject = reject
})
}
resolve = data => {
this.state = 'fullFilled'
let returnValue
if (this.onFullFilled) {
returnValue = this.onFullFilled(data)
}
if (this.nextResolve) {
this.nextResolve(returnValue)
}
}
reject = error => {
this.state = 'rejected'
if (this.onRejected) {
this.onRejected(error)
} else if (this.onCatch) {
this.onCatch(error)
} else {
this.nextReject(error)
}
}
catch = fn => {
this.onCatch = fn
}
}
const getData = new _Promise((resolve, reject) => {
setTimeout(() => {
// resolve('data')
reject('error')
}, 1000)
})
getData
.then(res => {
console.log('1', res)
})
.then(res => {
console.log('2', res)
}, err => {
console.log('2 err', err)
})
.catch(err => {
console.log('catch error', err)
})Promise.all的简单实现
javascript1
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
38Promise._all = promises => {
const resList = []
let count = 0
return new Promise((resolve, reject) => {
promises.forEach((item, index) => {
item
.then(res => {
count++
resList[index] = res
if (count === promises.length) {
resolve(resList)
}
}, error => {
reject(error)
})
})
})
}
const promise1 = new Promise((resolve, reject) => {
resolve(1);
// reject(1);
});
const promise2 = new Promise((resolve, reject) => {
resolve(2);
});
const promise3 = new Promise((resolve, reject) => {
resolve(3);
});
Promise._all([promise1, promise2, promise3])
.then(res => {
console.log(res);
}, error => {
console.log(error)
})Promise并发限制
javascript1
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
74class PromisePool {
constructor(promiseProducers, limit) {
/**
* promiseProducers promise生产者列表
* limit 最大并发数
* res 请求返回数据
* index 目前发起请求的index
* sum promise个数
* count promise成功个数
* resolve 全部promise成功之后的要执行的函数
*/
this.promiseProducers = promiseProducers
this.limit = limit
this.res = []
this.index = limit - 1
this.sum = promiseProducers.length
this.count = 0
this.resolve = null
}
// 启动并发请求
start() {
const curPromiseProducers = this.promiseProducers.splice(0, this.limit)
curPromiseProducers.forEach((item, index) => {
this.runTask(item, index)
})
return new Promise(resolve => {
this.resolve = resolve
})
}
// 单个请求
runTask(promiseProducer, index) {
promiseProducer().then(res => {
this.res[index] = res
this.count++
if (this.promiseProducers.length > 0) {
this.index++
this.runTask(this.promiseProducers.shift(), this.index)
}
if (this.count === this.sum) {
this.resolve(this.res)
}
})
}
}
const urls = [
'bytedance.com',
'tencent.com',
'alibaba.com',
'microsoft.com',
'apple.com',
'hulu.com',
'amazon.com',
]
const requestFn = url => {
return new Promise(resolve => {
setTimeout(() => {
resolve(`任务${url}数据`)
}, 1000)
})
}
const promiseProducers = urls.map(item => requestFn.bind(null, item))
// 最大并发数3
const pool = new PromisePool(promiseProducers, 3)
pool.start()
.then(res => {
console.log(res)
})
async/await 简单实现
javascript
1 | function autoRun(func) { |
new原理
javascript
1 | function _new(func, ...rest) { |
bind原理
javascript
1 | Function.prototype._bind = function (context, ...rest) { |
继承
构造函数继承
javascript1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21function Animal(name) {
this.name = name
}
Animal.prototype.sleep = function () {
console.log('sleep')
}
function Cat(name, color) {
Animal.call(this, name)
this.color = color
}
Cat.prototype = new Animal()
Cat.prototype.walk = function () {
console.log('walk')
}
const myCat = new Cat('my litter cat', 'black')
myCat.sleep()
myCat.walk()
console.log('name:', myCat.name, 'color: ', myCat.color)ES6 class语法继承
javascript1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25class Animal {
constructor(name) {
this.name = name
}
sleep() {
console.log('sleep')
}
}
class Cat extends Animal {
constructor(name, color) {
super(name)
this.color = color
}
walk() {
console.log('walk')
}
}
const myCat = new Cat('my litter cat', 'black')
myCat.sleep()
myCat.walk()
console.log('name:', myCat.name, 'color: ', myCat.color)对象关联(js继承的本质)
javascript1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18const animal = {
name: 'animal',
sleep() {
console.log('sleep')
}
}
const cat = {
name: 'cat',
walk() {
console.log('walk')
}
}
Object.setPrototypeOf(cat, animal)
console.log(cat.name)
cat.sleep()
React virtual DOM
html
1 | <div id="container" class="container"> |
shadow dom 的基本使用
html
1 | <div id="container" class="container"> |
Redux 简易版
javascript
1 | // API |
React-router-dom 简易版
javascript
1 | function Login() { |
Node.js 中间件
javascript
1 | // API |
排序算法
冒泡排序
javascript1
2
3
4
5
6
7
8
9
10
11function bubbleSort(arr) {
const list = [...arr]
for (let i = 0; i < list.length - 1; i++) {
for (let j = 0; j < list.length - i - 1; j++) {
if (list[j] > list[j + 1]) {
[list[j], list[j + 1]] = [list[j + 1], list[j]]
}
}
}
return list
}选择排序:找到数据结构中的最小值,并将其放在第一位,接著找第二小的值放在第二位,以此类推
javascript1
2
3
4
5
6
7
8
9
10
11
12
13
14function selectionSort(arr) {
const list = [...arr]
let minIndex = 0
for (let i = 0; i < list.length; i++) {
minIndex = i
for (let j = i; j < list.length; j++) {
if (list[j] < list[minIndex]) {
minIndex = j
}
}
[list[i], list[minIndex]] = [list[minIndex], list[i]]
}
return list
}插入排序:通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入
javascript1
2
3
4
5
6
7
8
9
10
11
12
13
14
15function insertionSort(arr) {
const list = [...arr]
for (let i = 0; i < arr.length; i++) {
const temp = list[i]
for (let j = i; j > 0; j--) {
if (temp < list[j - 1]) {
list[j] = list[j - 1]
} else {
list[j] = temp
break
}
}
}
return list
}归并排序O(nlog_n):将原数组切分成较小的数组,直到每个小数组只有一个位置,接着将小数组归并成较大的数组,直到最后只有一个排序完毕的大数组
javascript1
2
3
4
5
6
7
8
9
10
11
12
13
14
15function mergeSort(arr) {
const list = [...arr]
const merge = (leftList, rightList) => {
const mergeList = []
while (leftList.length && rightList.length) {
mergeList.push(leftList[0] < rightList[0] ? leftList.shift() : rightList.shift())
}
return mergeList.concat(leftList, rightList)
}
if (list.length < 2) return list
const mid = Math.floor(list.length / 2)
return merge(mergeSort(list.slice(0, mid)), mergeSort(list.slice(mid)))
}快速排序:在数组中间选择一个主元,交换两边的元素,让主元左边的元素小于主元,右边的元素大于主元;再对两边重复这个步骤;
javascript1
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
29function quickSort(array) {
const newArr = [...array]
const sort = (list, left, right) => {
if (left >= right) return
let i = left
let j = right
const base = list[left]
while (i < j) {
// 从右边起,寻找比基数小的数
while (i < j && list[j] >= base) {
j--
}
// 从左边起,寻找比基数大的数
while (i < j && list[i] <= base) {
i++
}
[list[i], list[j]] = [list[j], list[i]]
}
list[left] = list[i]
list[i] = base
sort(list, left, i - 1)
sort(list, i + 1, right)
}
sort(newArr, 0, newArr.length - 1)
return newArr
}堆排序:https://segmentfault.com/a/1190000015487916
javascript1
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
33function heapSort(arr) {
const list = [...arr]
const heapify = (list, i, size) => {
const left = 2 * i + 1
const right = 2 * i + 2
let maxIndex = i
if (left < size && list[left] > list[maxIndex]) {
maxIndex = left
}
if (right < size && list[right] > list[maxIndex]) {
maxIndex = right
}
if (maxIndex !== i) {
[list[i], list[maxIndex]] = [list[maxIndex], list[i]]
heapify(list, maxIndex, size)
}
}
const buildHeap = list => {
for (let i = Math.floor(list.length / 2) - 1; i >= 0; i--) {
heapify(list, i, list.length)
}
}
// 建大顶堆
buildHeap(list)
// 拿出堆顶(最大值),剩下的节点再调整成大顶堆
for (let size = list.length - 1; size > 0; size--) {
[list[0], list[size]] = [list[size], list[0]]
heapify(list, 0, size)
}
return list
}
发布订阅模式
javascript
1 | const Event = (function () { |
对象深拷贝
javascript
1 | const deepCopy = variable => { |
防抖和节流
javascript
1 | // 防抖 |
解题
请实现 find 函数,使下列的代码调用正确
javascript1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 约定:
// title数据类型为String
// userId为主键,数据类型为Number
var data = [
{userId: 8, title: 'title1'},
{userId: 11, title: 'other'},
{userId: 15, title: null},
{userId: 19, title: 'title2'}
];
var find = function(origin) {
// your code are here...
}
// 查找 data 中,符合条件的数据,并进行排序
var result = find(data).where({
'title': /\d$/
}).orderBy('userId', 'desc');
console.log(result);// [{ userId: 19, title: 'title2'}, { userId: 8, title: 'title1' }];javascript1
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
27class MyArray extends Array {
where = filterOpt => {
let list = this
for (let key of Object.keys(filterOpt)) {
list = this.filter(item => {
return filterOpt[key].test(item[key])
})
}
return list
}
orderBy = (orderKey, orderType) => {
let list = this
list = this.sort((a, b) => {
if (orderType === 'desc') {
return b[orderKey] - a[orderKey]
} else {
return a[orderKey] - b[orderKey]
}
})
return list
}
}
var find = function (origin) {
return new MyArray(...origin)
}实现一个求和函数sum,支持任意参数: sum(1), sum(1,2,3,4); 支持连续调用: sum(1)(2)(3)。如下所示:
javascript1
2const result = sum(1)(2,3)(4);
console.log(result) // 输出 10javascript1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19function sum(...params) {
let numList = params
let res = 0
const returnFn = (...childParams) => {
numList.push(...childParams)
return returnFn
}
// console.log会调用参数的toString方法
returnFn.toString = () => {
res = numList.reduce((total, item) => {
return total + item
})
return res
}
return returnFn
}