# Js基础总结
# 1. 去除字符串两边空格
const trim = (str, type) => {
const regObj = {
left: /^\s+/,
right: /\s+$/,
both: /(^\s+)|(\s+$)/g,
all: /\s+/g,
};
const reg = (type && regObj[type]) ?? regObj.both;
return str.replace(reg, '');
};
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 2. 去除字符串中最后一个指定的字符
const delLast = (str, target) => {
const reg = new RegExp(`${target}(?=([^${target}]*)$)`);
return str.replace(reg, '');
};
const delLast1 = (str, target) => {
return str.split('').reverse().join('').replace(target, '')
.split('')
.reverse()
.join('');
};
const delLast2 = (str, target) => {
const index = str.lastIndexOf(target);
return str.substring(0, index) + str.substring(index + 1, str.length);
};
console.log(delLast('Hello world!', 'o')); // Hello wrld!
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
# 3. 字符串大小写切换
const caseConvert = (str) => {
return str.replace(/([a-z]*)([A-Z]*)/g, (m, s1, s2) => {
return `${s1.toUpperCase()}${s2.toLowerCase()}`;
});
};
console.log(caseConvert('AbCdE')); // aBcDe
1
2
3
4
5
6
2
3
4
5
6
# 4. 去除制表符和换行符的方法
/**
* \v 匹配垂直制表符
* \n 换行符 new line
* \r 回车符 return
* \t 制表符 tab
* \f 换页符 form feed
* \b 退格符 backspace
* @param str
* @returns {void | string}
*/
const removeEmpty = (str) => str.replace(/[\t\n\v\r\f]/g, '');
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 5. 统计某一字符(串)出现的次数
统计某一字符或字符串在另一个字符串中出现的次数
const strCount = (str, target) => str.split(target).length - 1;
1
# 6. script 标签中 defer 和 async 的区别
script:会阻碍 HTML 解析,只有下载好并执行完脚本才会继续解析 HTML。async script:解析 HTML 过程中进行脚本的异步下载,下载成功立马执行,有可能会阻断 HTML 的解析,DOMContentLoaded和async脚本不会互相等待defer script:完全不会阻碍 HTML 的解析,解析完成之后再按照顺序执行脚本(DOMContentLoaded事件处理程序等待defer脚本执行完之后执行)
| 标签 | js执行顺序 | 阻塞解析HTML |
|---|---|---|
| scipt | 在HTML中的顺序 | 阻塞 |
| scipt defer | 在HTML中的顺序 | 不阻塞解析 |
| scipt async | 网络请求返回顺序 | 可能阻塞,也可能不阻塞,取决于什么时候下载完成 |

# 7. 实现一个带并发限制的异步调度器 Scheduler,保证同时运行的任务最多有两个
// 实现一个带并发限制的异步调度器 Scheduler,保证同时运行的任务最多有两个
class Scheduler {
// Your code here
constructor(limit) {
this.limit = limit;
this.queue = [];
this.running = 0;
}
add(task) {
return new Promise((resolve, reject) => {
this.queue.push({ task, resolve, reject });
this.run();
});
}
run() {
while (this.running < this.limit && this.queue.length) {
const { task, resolve, reject } = this.queue.shift();
this.running += 1;
task().then(resolve).catch(reject).finally(() => {
this.running -= 1;
this.run();
});
}
if (this.running === 0 && this.queue.length === 0) {
console.log('All tasks completed');
}
}
}
const delay = (timeout) => new Promise((resolve) => {
setTimeout(resolve, timeout);
});
const scheduler = new Scheduler(2);
const addTask = (time, order) => {
scheduler.add(() => delay(time)).then(() => console.log(order));
};
addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');
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
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
# 8. 实现一个数据打乱的函数
# sort
大部分会使用 sort 结合 Math.random,这种方式并不是真正意思上的乱序,一些元素并没有机会相互比较, 最终数组元素停留位置的概率并不是完全随机的
// 数组乱序
const shuffle = (arr) => {
return arr.sort(() => Math.random() - 0.5);
};
1
2
3
4
2
3
4
v8在处理sort方法时,使用了插入排序和快排两种方案。当目标数组长度小于10时,使用插入排序;反之,使用快速排序。其实不管用什么排序方法,大多数排序算法的时间复杂度介于O(n)到O(n²)之间,元素之间的比较次数通常情况下要远小于n(n-1)/2,也就意味着有一些元素之间根本就没机会相比较(也就没有了随机交换的可能),这些 sort 随机排序的算法自然也不能真正随机
改造 sort 和 Math.random() 的结合方式
const shuffle = (arr) => {
const newArr = arr.map((item) => ({ val: item, ram: Math.random() }));
newArr.sort((a, b) => a.ram - b.ram);
arr.splice(0, arr.length, ...newArr.map((i) => i.val));
return arr;
};
1
2
3
4
5
6
2
3
4
5
6
# Fisher–Yates
这个算法其实非常的简单,就是将数组从后向前遍历,然后将当前元素与随机位置的元素进行交换
const shuffle = (arr) => {
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
};
1
2
3
4
5
6
7
2
3
4
5
6
7
← CSS之Grid布局 Js基础数据类型 →