# 51.id选择器问题
- 《你不知道的JS(中)》p126
- id选择器会在全局创建一个同名变量
<body>
<div id="foo"></div>
<script>
console.log(foo); // div#foo (DOM元素)
</script>
</body>
1
2
3
4
5
6
7
2
3
4
5
6
7
- var声明后有会把它覆盖掉
<body>
<div id="foo">啦啦啦</div>
<script>
console.log(foo); // undefined
if (typeof foo === 'undefined') {
var foo = 100;
}
console.log(foo); // 100
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 52.Worker作用
- 《你不知道的JS(中)》p288
- JS是单线程语言,所有的执行都是
单线程操作
- 因此JS的异步不是像JAVA那样多开一个thread线程来进行操作,而是使用
事件轮询机制
- 遇到问题:一个
同步操作
涉及大量计算时程序会卡死:
点击程序卡死按钮后计算器不能正常运行操作,因为单线程正忙着呢
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Worker的作用</title>
</head>
<body>
<button id="temp">点我程序卡死十秒</button>
<div>计算器</div>
<button id="del">-</button>
<div id="num">0</div>
<button id="add">+</button>
<script>
temp.addEventListener('click', () => {
const end = Date.now() + 10000;
console.log('大量计算中');
while (Date.now() < end) {}
console.log('计算完毕');
});
del.addEventListener('click', () => {
num.innerText = +num.innerText - 1;
})
add.addEventListener('click', () => {
num.innerText = +num.innerText + 1;
})
</script>
</body>
</html>
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
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
- worker给JS执行提供多线程操作空间
worker应该是一个
网络文件
,属于一个半封闭环境,里面可以定义一些方法,和指定的事件监听钩子
const express = require('express');
const fs = require('fs');
const path = require('path');
const app = express();
app.listen('8888', () => console.log('listen 8888'));
app.get('/', (req, res) => {
res.setHeader('Content-Type', 'text/html')
const url = path.join(__dirname, './test.html');
const code = fs.readFileSync(url);
return res.send(code);
})
app.get('/worker', (req, res) => {
res.setHeader('Content-Type', 'text/javascript')
const url = path.join(__dirname, './worker.js');
const code = fs.readFileSync(url);
res.send(code)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- 将大量计算的代码放到这个空间里面,空间里面不能涉及DOM操作、全局window对象获取、跨域等都有限制
onmessage = e => {
console.log(e.data);
const end = Date.now() + 10000;
console.log('大量计算中');
while (Date.now() < end) { }
console.log('计算完毕');
postMessage({ num: end });
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
- 有大量耗时操作的地方传到worker线程去计算,计算玩返回给JS主线程结果,让主线程不被卡死
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Worker的作用</title>
</head>
<body>
<button id="temp">点我程序卡死十秒</button>
<div>计算器</div>
<button id="del">-</button>
<div id="num">0</div>
<button id="add">+</button>
<script>
const worker = new Worker('./worker');
worker.onmessage = e => console.log(e);
temp.addEventListener('click', () => {
worker.postMessage('add');
});
del.addEventListener('click', () => {
num.innerText = +num.innerText - 1;
})
add.addEventListener('click', () => {
num.innerText = +num.innerText + 1;
})
</script>
</body>
</html>
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
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
# 53.正则2
- 正则检查数字,最多12个数字,小数点后最多三个
const a = '123.33'; // true
const b = '1223.3333'; // false
const c = '324123.033'; // true
const d = '324123'; // true
const e = '123456789012'; // true
const f = '1234567890122'; // false
const g = '12345678.9012'; // false
const h = '12345678.012'; // true
console.log(valNum(a));
console.log(valNum(b));
console.log(valNum(c));
console.log(valNum(d));
console.log(valNum(e));
console.log(valNum(f));
console.log(valNum(g));
console.log(valNum(h));
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
- 或【|】表达式需要用括号括起来,而不需要捕获这段括号,用
环视
function valNum(num) {
const reg1 = /^(\d{0,9})(?:\d{0,3}|\d{0,2}\.\d|\d?\.\d{2}|\.\d{3})$/;
return reg1.test(num);
}
1
2
3
4
2
3
4
# 54.super对象
《你不知道的JS(下)》p97
const a = {
name: 'hdy',
say() {
console.log(this.name);
}
};
const b = {
age: 18,
say: function () {
super.say();
console.log(this.age);
}
}
Object.setPrototypeOf(b, a);
b.say(); // 输出?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- super关键字只能在
简洁方法
中使用,所以function直接调用会报错
const a = {
name: 'hdy',
say() {
console.log(this.name);
}
};
const b = {
age: 18,
// say: function () {
say() {
super.say();
console.log(this.age);
}
}
Object.setPrototypeOf(b, a);
b.say();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
简洁方法:将对象中的function省略掉,直接写函数形式
# 55.Symbol问题
- 《你不知道的JS(下)》p125
- 输出与原因
const age = Symbol('age');
const obj = {
name: 'hdy',
age: 18,
[age]: 19
}
console.log(obj.age);
console.log(obj[age]);
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
- Symbol是独一无二的存在,有避免混淆的作用,所以如果要创建一个避免被覆盖的变量,可以用Symbol
- 同时要注意作用域,如果要访问该变量,就要让Symbol也能被访问到的作用域
# 56.首屏渲染时间
- 最精确的开始时间:
performance.timeOrigin
- (慢几十毫秒)首屏index头部加上script
Date.now()
- css加载、html加载、图片加载、script加载
- 每个url请求元素都可以绑定
onload
事件拿到加载完毕事件,如img - 整体加载完毕
window.onload
,加载文件完成后就可以渲染,这里可拿到白屏时间
- 页面渲染
- 加载完后js执行,可能会渲染元素上屏,如:vue组件内有图片,图片地址需要ajax异步得到,图片加载完毕后拿到的是
首屏时间
完全静态的文件可以通过
window.onload
事件中Date.now() - performance.timeOrigin
计算整个文件的加载时间,渲染时间根据浏览器性能差异会慢一点
现在的框架基本都是动态前端页面,所以需要动态计算
,并且首屏时间应该是用户能看到的首屏渲染时间,与看不到的内容无关
- 页面加载完成,css和html组成
render tree
的时候,各元素的值已经可以正确获取,可以通过ele.getBoundingClientRect()
或ele.offsetTop叠加计算
获取
function inFirstScreen(ele) {
const rect = ele.getBoundingClientRect();
const screenWidth = window.innerWidth;
const screenHeight = window.innerHeight;
const rectLeft = rect.left;
const rectTop = rect.top;
return rectLeft <= screenWidth && rectTop <= screenHeight;
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
注:获取dom的offset等
渲染坐标、大小等
数据会触发页面重排,先取出来再使用,提升性能
- 一般影响主要渲染时间的是图片等多媒体element,所以一般计算首页图片加载完毕的时间基本就可以判定为首屏时间
const imgs = Array.from(document.getElementsByTagName('img'));
imgs.forEach(item => {
if (inFirstScreen(item)) {
item.addEventListener('load', () => {
if (inFirstScreen(item)) {
console.log(Date.now() - performance.timeOrigin);
}
});
}
})
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
- 判断了两次
inFirstScreen
:- 第一次是首屏元素判断,说明这个元素可能出现在首屏中,防止添加过多无效的事件
- 加载完毕后再判断一次,说明此元素一定是在首屏中,因为可能会被别的没提前设置高度的元素(如图片)给挤到屏幕外面去。
- 静态计算适合于首页:img标签本来就有,但是具体url是动态获取的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, viewport-fit=cover, user-scalable=no">
<meta charset="UTF-8">
<style>
#temp {
height: 100px;
width: 100px;
background-color: rgb(192, 41, 41);
}
#img2 {
height: 100px;
}
</style>
<title>首屏时间计算</title>
</head>
<body>
<button id="temp">啦啦啦啦</button>
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<img src="" alt="" id="img">
<img src="" alt="" id="img2">
<script>
setTimeout(() => img.setAttribute('src', 'https://coderhdy.com/assets/img/bgimg.jpg'), 1000);
setTimeout(() => img2.setAttribute('src', 'https://coderhdy.com/assets/img/bgimg.jpg'), 1000);
const firstScreenTime = new Promise(resolve => {
const inFirstScreen = (ele) => {
const rect = ele.getBoundingClientRect();
const screenWidth = window.innerWidth;
const screenHeight = window.innerHeight;
const rectLeft = rect.left;
const rectTop = rect.top;
return rectLeft <= screenWidth && rectTop <= screenHeight;
}
const imgs = Array.from(document.getElementsByTagName('img'));
let ans = 0;
imgs.forEach(item => {
if (inFirstScreen(item)) {
item.addEventListener('load', () => {
if (inFirstScreen(item)) {
ans = Date.now() - performance.timeOrigin;
}
});
}
});
setTimeout(() => resolve(ans), 5000)
});
firstScreenTime.then(res => console.log(res));
</script>
</body>
</html>
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
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
- 在首页中,如果有列表或判断类动态渲染出img数量的标签,就需要动态计算,因为不确定是什么时候添加进来的,就不能在最开始执行的时候添加事件监听。
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, viewport-fit=cover, user-scalable=no">
<meta charset="UTF-8">
<style>
#temp {
height: 100px;
width: 100px;
background-color: rgb(192, 41, 41);
}
img {
display: inline-block;
width: 120px;
height: 200px;
padding: 20px;
}
</style>
<title>首屏时间计算</title>
</head>
<body>
<button id="temp">啦啦啦啦</button>
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<div id="imgs">
</div>
<script>
// 定时添加不定量图片,可能会溢出屏幕
new Promise(resolve => {
setTimeout(resolve, 1000)
}).then(() => {
const imgNum = Math.ceil(Math.random() * 10) + 1;
for (let i = 0; i < imgNum; i++) {
const img = document.createElement('img');
img.setAttribute('src', 'https://coderhdy.com/assets/img/bgimg.jpg');
imgs.appendChild(img);
}
})
const firstScreenTime = new Promise(resolve => {
const inFirstScreen = (ele) => {
const rect = ele.getBoundingClientRect();
const screenWidth = window.innerWidth;
const screenHeight = window.innerHeight;
const rectLeft = rect.left;
const rectTop = rect.top;
return rectLeft <= screenWidth && rectTop <= screenHeight;
}
const timeAdder = item => {
if (inFirstScreen(item)) {
item.addEventListener('load', () => {
if (inFirstScreen(item)) {
ans = Date.now() - performance.timeOrigin;
}
});
}
}
const imgs = Array.from(document.getElementsByTagName('img'));
let ans = 0;
imgs.forEach(timeAdder);
// 修改原生的appendChild
const native = Element.prototype.appendChild;
Element.prototype.appendChild = function (ele) {
native.call(this, ele);
timeAdder(ele);
}
setTimeout(() => {
resolve(ans);
// 使用完毕改回去
Element.prototype.appendChild = native;
}, 5000)
});
firstScreenTime.then(res => console.log(res));
</script>
</body>
</html>
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
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
# 57.addEventListener移除
<body>
<button id="temp">啦啦啦啦</button>
<script>
function c() {
console.log('---')
}
temp.addEventListener('click', c);
setTimeout(() => temp.removeEventListener('click', c), 3000)
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
- 第三个
options
参数设置signal,通过AbortController控制
<body>
<button id="temp">啦啦啦啦</button>
<script>
function c() {
console.log('---')
}
const controler = new AbortController();
setTimeout(() => controler.abort(), 3000)
temp.addEventListener('click', c, {
signal: controler.signal
});
</script>
</body>
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
- 第三个
options
参数设置once,触发一次后自动移除
<body>
<button id="temp">啦啦啦啦</button>
<script>
function c() {
console.log('---')
}
temp.addEventListener('click', c, {
once: true
});
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 58.红绿灯
- 实现红绿灯交替打印的异步函数
trafic(3000, 1000, 2000);
// red 停3秒 yellow 停1秒 green 停2秒 循环
console.log('同步代码不阻塞');
1
2
3
4
2
3
4
- await返回的是
Promise.resolve
包装的一个promise
,所以用promise控制时间流转
async function trafic(red, yellow, green) {
const sleep = time => new Promise(resolve => setTimeout(resolve, time));
while (true) {
console.log('red');
await sleep(red);
console.log('yellow');
await sleep(yellow);
console.log('green');
await sleep(green);
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
- while增加一个变量控制结束,将变量的控制器返回出去。
function trafic(red, yellow, green) {
const sleep = time => new Promise(resolve => setTimeout(resolve, time));
let stop = false;
const controller = () => stop = true;
const timer = async () => {
while (!stop) {
console.log('red');
await sleep(red);
if (stop) break;
console.log('yellow');
await sleep(yellow);
if (stop) break;
console.log('green');
await sleep(green);
}
}
timer();
return controller;
}
const stop = trafic(3000, 1000, 2000);
setTimeout(stop, 10000);
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
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
trafic
函数是正常函数,timer
函数是异步函数,被async包装成了promise,然而内部的循环控制器controller
被trafic
函数返回了出去。
const light = async (color, timer) => {
console.log(`当前是${color}`);
let cTime = timer;
console.log(`${cTime--}S`);
return new Promise(resolve => {
const interval = setInterval(() => console.log(`${cTime--}S`), 1010);
setTimeout(() => {
resolve();
clearInterval(interval);
}, timer * 1000);
});
}
const startLight = async () => {
await light('红', 3);
await light('黄', 1);
await light('绿', 3);
startLight();
}
startLight();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 59.+优先级
var val = 'smtg';
console.log('Value is ' + (val === 'smtg') ? 'Something' : 'Nothing'); // ??
1
2
2
// + 优先级优于 三元运算符
// Something
1
2
2
# 60.节流事件
- 写一个组件,上面显示当前窗口的 width/height
- 要求:
- resize同时要监听
- 监听器的移除
- 节流
- app模拟添加、移除size组件
- size组件显示当前视口宽高
- mounted添加事件监听,beforeDestroy移除监听
- methods里面方法this是绑定好的
<body>
<div id="app">
<button @click="toggle">toggle</button>
<size v-if="show"></size>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const size = {
template: `
<div>
width: {{width}}
<hr />
height: {{height}}
</div>
`,
data() {
return {
width: 0,
height: 0,
listener: null,
}
},
mounted() {
this.width = window.innerWidth;
this.height = window.innerHeight;
this.listener = this.resize()
window.addEventListener('resize', this.listener);
},
methods: {
resize() {
let timer;
return () => {
if (timer) return;
timer = setTimeout(() => {
console.log('resize');
this.width = window.innerWidth;
this.height = window.innerHeight;
timer = null;
}, 100);
}
}
},
beforeDestroy() {
console.log('destroy');
window.removeEventListener('resize', this.listener);
}
};
const app = new Vue({
el: '#app',
components: {
size
},
data() {
return {
show: true,
}
},
methods: {
toggle() {
this.show = !this.show;
}
}
});
</script>
</body>
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
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
# 61.instanceof
console.log('sdf' instanceof String);
1
- 字面量
string
/number
/boolean
本质上不是对象,是基础类型,没有原型链,只不过js在处理字面量调用方法的时候会将其包装成对象来进行方法的调用
console.log('sdf' instanceof String); // false
console.log(new String('sdf') instanceof String); // true
1
2
2
# 62.this2
- 这两段代码在浏览器的异同
function A() {
this.say = function () { console.log(this.name) };
}
window.name = 'iii';
const a = new A();
const say = a.say;
say()
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
class A {
say() { console.log(this.name) };
}
window.name = 'iii';
const a = new A();
const say = a.say;
say()
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
- 在浏览器中,class属于是es6,es6里函数默认调用的时候this不会指向window
# 63.数组转对象
const arr = ['a', 'b', 'c', 'd', 'e', 'f'];
console.log(modify(arr));
// {'a': {'b': {'c': {'d': {'e': 'f'}}}}}
1
2
3
2
3
function modify(arr) {
return arr.reduceRight((pre, item) => pre === null ? item : { [item]: pre }, null);
}
1
2
3
2
3
# 64.收集所有的DOM元素
- 输出页面所有的DOM元素的tag(去重)
<body>
<div class="container">
<p>
<span></span>
</p>
</div>
<hr>
<form action="">
<input type="text">
</form>
<script>
function getAllTags() {
const ans = new Set();
const handler = (ele) => {
ans.add(ele.tagName);
const children = ele.children;
Array.prototype.forEach.call(children, item => handler(item));
}
handler(document.querySelector('body'));
return [...ans];
}
console.log(getAllTags());
</script>
</body>
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
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
# 65.多维数组全排列
const arr = [['A', 'B'], ['a', 'b'], [1, 2]]
console.log(sort(arr));
// [
// 'Aa1', 'Aa2',
// 'Ab1', 'Ab2',
// 'Ba1', 'Ba2',
// 'Bb1', 'Bb2'
// ]
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
function sort(arr) {
const ans = [];
const handler = (i, str) => {
if (i === arr.length) return ans.push(str);
for (let j = 0; j < arr[0].length; j++) {
handler(i + 1, `${str}${arr[i][j]}`)
}
};
handler(0, '');
return ans;
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 66.交并集
function test(arr1, arr2) {
return [...(new Set([...arr1, ...arr2]))];
}
const arr1 = [1, 2, 3, 4, 5, 6];
const arr2 = [19, 23, 3, 41, 5, 6];
console.log(test2(arr1, arr2))
1
2
3
4
5
6
7
2
3
4
5
6
7
function test2(arr1, arr2) {
return arr1.filter(item => arr2.includes(item));
}
const arr1 = [1, 2, 3, 4, 5, 6];
const arr2 = [19, 23, 3, 41, 5, 6];
console.log(test2(arr1, arr2))
1
2
3
4
5
6
7
2
3
4
5
6
7
# 67.手写继承
function Parent(data) {
this.data = data;
}
Parent.prototype.foo = function () {
console.log(this.data);
}
function inherit(Foo) {
// TODO
}
var child = new (inherit(Parent))(123);
child.foo(); // 123;
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
- 注意this传给父函数
- 注意原型链拼接时的constructor保持
function inherit(Foo) {
const Child = function (...args) {
Foo.call(this, ...args);
}
Child.prototype = Object.create(Foo.prototype);
Child.prototype.constructor = Child;
return Child;
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 68.localStorate
- 设置一个原型方法,让localstorage有set和get方法,比setItem、getItem更好用,能够判断是否有时效性
localStorage.set('name', 'hdy', 1000 * 10);
localStorage.set('age', 18, 1000 * 20);
setTimout(() => console.log(localStorage.get(name)), 9000); // hdy
setTimout(() => console.log(localStorage.get(name)), 11000); // null
1
2
3
4
5
2
3
4
5
<body>
<script>
localStorage.__proto__.get = key => {
const item = localStorage.getItem(key);
let val;
try {
val = JSON.parse(item);
} catch (e) {
val = null;
}
if (val === null || val.end < Date.now()) {
localStorage.removeItem(key);
return null;
}
return val.data;
}
localStorage.__proto__.set = (key, data, time = 1000 * 5) => {
const val = {
end: Date.now() + time,
data
}
localStorage.setItem(key, JSON.stringify(val));
}
localStorage.set('name', 'hdy', 1000 * 10);
localStorage.set('age', 18, 1000 * 20);
setTimeout(() => console.log(localStorage.get("name")), 9000); // hdy
setTimeout(() => console.log(localStorage.get("name")), 11000); // null
</script>
</body>
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
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
# 69.手写JSONP实现
<body>
<script>
function callback(data) {
console.log(data);
}
(function () {
const script = document.createElement('script');
script.src = 'http://localhost:8090';
document.body.appendChild(script);
})();
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
const express = require('express');
const app = new express();
app.listen(8090);
const data = {
name: 'hdy',
age: 18
}
app.get('/', (req, res) => {
res.send(`callback(${JSON.stringify(data)})`);
});
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 70.无符号计算
- 力扣 (opens new window)
- 计算
1 + 2 + 3...+ n
,不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
- 推到已知公式为
(n + 1) * n / 2
- repeat可以做乘法
- 右移运算符 >> 可以做除法,相当于除以Math.pow(2, x);
var sumNums = function(n) {
return ' '.repeat(n + 1).repeat(n).length >> 1;
};
1
2
3
2
3
- 证明
function test(n, y) {
return Math.floor(n / Math.pow(2, y)) === n >> y;
}
console.log(test(16, 2));
1
2
3
4
5
2
3
4
5
# 71.简易JSX compiler
const str = `<MyButton color="blue" shadowSize={2}>Click Me</MyButton>`;
console.log(compiler(str));
// {
// tagName: 'MyButton',
// props: { color: 'blue', shadowSize: '2' },
// content: 'Click Me'
// }
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
function compiler(jsx) {
const tagReg = /(?<=\s*\<)([A-Z]\w*)([^\>]*)\>(.*)<\/\1>/s;
const getProps = row => {
const props = {};
row = row.trim();
if (row === '') return props;
row.split(/\s+/).forEach(item => {
const [key, val] = item.split(/\s*=\s*/);
props[key] = val.match(/^['"{](.*)['"}]/)[1];
})
return props;
}
if (tagReg.test(jsx)) {
const [_, tagName, rowProps, content] = (tagReg.exec(jsx));
const props = getProps(rowProps);
return { tagName, props, content }
} else {
return '';
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 72.链式函数
const pipFn = (val) => {}
const add = (val) => val + 2;
const sub = (val) => val - 2;
const double = (val) => val * 2;
const pow = (val) => Math.pow(val, val);
pipFn(2).double.pow.do; // 256
1
2
3
4
5
6
7
2
3
4
5
6
7
const pipFn = (val) => {
const fns = [];
const proxy = new Proxy({}, {
get(target, p) {
if (p === "do") {
return fns.reduce((pre, item) => eval(`${item}(${pre})`), val);
} else {
fns.push(p);
}
return proxy;
}
})
return proxy;
}
const add = (val) => val + 2;
const sub = (val) => val - 2;
const double = (val) => val * 2;
const pow = (val) => Math.pow(val, val);
console.log(pipFn(2).double.pow.do); // 256
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 73.自定义事件
<body>
<button id="add"> +1 </button>
<script>
const addBtn = document.querySelector("#add");
addBtn.addEventListener("click", () => {
let num = localStorage.getItem("num")
if (!num) {
num = 0;
}
num = +num + 1;
localStorage.setItem("num", num);
});
/* 通过自定义事件让 localStorage 变成响应式 */
(() => {
const oritinItem = localStorage.setItem;
window.localStorage.setItem = (key, val) => {
const setItemEvent = new CustomEvent("setLocalStorage", { detail: { key, val } });
dispatchEvent(setItemEvent);
oritinItem.call(window.localStorage, key, val)
}
})()
window.addEventListener("setLocalStorage", e => alert(JSON.stringify(e.detail)));
</script>
</body>
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
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
# 74.命名小驼峰转下划线
const oldName = "getPropertyByName";
const change = (oldName) => {}
const newName = change(oldName);
console.log(newName); // get_property_by_name
1
2
3
4
5
6
2
3
4
5
6
const oldName = "getPropertyByName";
const change = (oldName) => oldName.replace(/([A-Z])/g, (i) => `_${i.toLowerCase()}`)
const newName = change(oldName);
console.log(newName); // get_property_by_name
1
2
3
4
5
6
2
3
4
5
6
# 74.命名下划线转小驼峰
const oldName = "get_property_by_name";
const change = (oldName) => {}
const newName = change(oldName);
console.log(newName); // getPropertyByName
1
2
3
4
5
6
2
3
4
5
6
const oldName = "get_property_by_name";
const change = (oldName) => oldName.replace(/_([a-z])/g, (_, i) => `${i.toUpperCase()}`)
const newName = change(oldName);
console.log(newName); // getPropertyByName
1
2
3
4
5
6
2
3
4
5
6
# 75.数组转树
const arr = [
{
id: 2,
name: '部门B',
parentId: 0
}, {
id: 3,
name: '部门C',
parentId: 1
}, {
id: 1,
name: '部门A',
parentId: 2
}, {
id: 4,
name: '部门D',
parentId: 1
}, {
id: 5,
name: '部门E',
parentId: 2
}, {
id: 6,
name: '部门F',
parentId: 3
}, {
id: 7,
name: '部门G',
parentId: 2
}, {
id: 8,
name: '部门H',
parentId: 4
}
]
const obj = arr2Tree(arr);
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
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
- output:
const obj = {
id: 2,
name: '部门B',
parentId: 0,
children: [
{
id: 1,
name: '部门A',
parentId: 2,
children: [
{
id: 4,
name: '部门D',
parentId: 1,
children: [
{
id: 8,
name: '部门H',
parentId: 4
}
]
}, {
id: 3,
name: '部门C',
parentId: 1,
children: [
{
id: 6,
name: '部门F',
parentId: 3
}
]
},
]
}, {
id: 5,
name: '部门E',
parentId: 2,
}, {
id: 7,
name: '部门G',
parentId: 2
}
]
}
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
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
const arr2Tree = arr => {
const leader = arr.reduce((pre, item) => pre.parentId < item.parentId ? pre : item, arr[0]);
const deepFn = (parent) => {
for (let i = arr.length - 1; i > 0; i--) {
if (arr[i].id == parent.id) {
arr.splice(i, 1);
continue;
}
if (arr[i].parentId === parent.id) {
parent.children = parent.children ? [...parent.children, arr[i]] : [arr[i]];
arr.splice(i, 1);
}
}
parent.children && parent.children.forEach(item => deepFn(item));
}
deepFn(leader);
return leader;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 76.限制请求数量
const reqs = [
() => new Promise((resolve) => setTimeout(() => resolve(1), 1000)).then(res => console.log(res)),
() => new Promise((resolve) => setTimeout(() => resolve(2), 1000)).then(res => console.log(res)),
() => new Promise((resolve) => setTimeout(() => resolve(3), 1000)).then(res => console.log(res)),
() => new Promise((resolve) => setTimeout(() => resolve(4), 1000)).then(res => console.log(res)),
() => new Promise((resolve) => setTimeout(() => resolve(5), 1000)).then(res => console.log(res)),
() => new Promise((resolve) => setTimeout(() => resolve(6), 1000)).then(res => console.log(res)),
() => new Promise((resolve) => setTimeout(() => resolve(7), 1000)).then(res => console.log(res)),
() => new Promise((resolve) => setTimeout(() => resolve(8), 1000)).then(res => console.log(res)),
() => new Promise((resolve) => setTimeout(() => resolve(9), 1000)).then(res => console.log(res)),
() => new Promise((resolve) => setTimeout(() => resolve(10), 1000)).then(res => console.log(res)),
]
requestBantch(reqs, 5);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
const requestBantch = async (requestFns, maxOnce) => {
const request = async () => {
let current = requestFns.splice(0, maxOnce);
await Promise.allSettled(current.map(fn => fn())).then((ansArr) => {
console.log(ansArr);
});
}
while (requestFns.length > 0) {
await request();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 77.中文读法转换
// const num = 123456789012;
const num = 123450000099;
// const num = 423042340234;
console.log(num2String(num)); // 一千二百三十四亿五千六百七十八万九千零一十二
// 一千二百三十四亿五千万零九十九
// 四千二百三十亿四千二百三十四万零二百三十四
1
2
3
4
5
6
7
2
3
4
5
6
7
// const num = 123456789012;
const num = 123450000099;
// const num = 423042340234;
const num2String = (num) => {
const unit = [ "千", "百", "十", "亿", "千", "百", "十", "万", "千", "百", "十", ""];
const ansArr = num.toLocaleString("zh-Hans-CN-u-nu-hanidec", {useGrouping: false}).split("");
const ans = ansArr.map((item, i) => {
if (item === "〇") {
return i === ansArr.length - 1 ? "" : (["亿", "万"].includes(unit[i]) ? unit[i] : "零");
} else {
return `${item}${unit[i]}`
}
}).join("").replace(/^零+|(?<=零)零+|零+(?=[亿万])/g, "");
return ans;
}
console.log(num2String(num)); // 一千二百三十四亿五千六百七十八万九千零一十二
// 一千二百三十四亿五千万零九十九
// 四千二百三十亿四千二百三十四万零二百三十四
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 78.去除字符串最少重复字符
// const str = "ababac"; // ababa
const str = "aaabbbcceeff"; // aaabbb
console.log(removeLessStr(str));
1
2
3
4
2
3
4
// const str = "ababac"; // ababa
const str = "aaabbbcceeff"; // aaabbb
const removeLessStr = (str) => {
const map = Array.prototype.reduce.call(str, (pre, c) => {
pre.set(c, (pre.has(c) ? pre.get(c) + 1 : 1));
return pre;
}, new Map());
const lessNum = [...map.values()].reduce((pre, item) => Math.min(item, pre), Number.MAX_SAFE_INTEGER);
let deleteChars = [...map.entries()].filter(([k, v]) => v === lessNum).map(([k, v]) => k);
const ans = str.split("").filter(item => !deleteChars.includes(item)).join("");
return ans;
}
console.log(removeLessStr(str));
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
# 79.归属数组
const groups = {
A: [1,2,3],
B: [4,5,6],
C: [7,8,9],
}
console.log(test(8)); // C
1
2
3
4
5
6
7
2
3
4
5
6
7
const groups = {
A: [1,2,3],
B: [4,5,6],
C: [7,8,9],
}
function test(num) {
const map = new Map(Object.entries(groups));
return [...map.entries()].find(([k, v]) => v.includes(num))[0]
}
console.log(test(8)); // C
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 80.全排列
const strArrs = [["A", "B", "C"], ["1", "2", "3"], ["a", "b", "c"]];
console.log(joinArrs(strArrs));
// [
// 'A1a', 'A1b', 'A1c', 'A2a',
// 'A2b', 'A2c', 'A3a', 'A3b',
// 'A3c', 'B1a', 'B1b', 'B1c',
// 'B2a', 'B2b', 'B2c', 'B3a',
// 'B3b', 'B3c', 'C1a', 'C1b',
// 'C1c', 'C2a', 'C2b', 'C2c',
// 'C3a', 'C3b', 'C3c'
// ]
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
const strArrs = [["A", "B", "C"], ["1", "2", "3"], ["a", "b", "c"]];
const joinArrs = (arr) => {
const ans = [];
if (arr.length === 0) return ans;
const deepFn = (current, needArr) => {
if (needArr.length === 0) {
ans.push(current);
} else {
needArr[0].forEach(item => deepFn(`${current}${item}`, needArr.slice(1)));
}
}
deepFn("", arr);
return ans;
}
console.log(joinArrs(strArrs));
// [
// 'A1a', 'A1b', 'A1c', 'A2a',
// 'A2b', 'A2c', 'A3a', 'A3b',
// 'A3c', 'B1a', 'B1b', 'B1c',
// 'B2a', 'B2b', 'B2c', 'B3a',
// 'B3b', 'B3c', 'C1a', 'C1b',
// 'C1c', 'C2a', 'C2b', 'C2c',
// 'C3a', 'C3b', 'C3c'
// ]
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
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
# 81.单数组全排列
const arr = [1, 2, 3];
console.log(fullAlignment(arr));
// [ [ 1 ], [ 1, 2 ], [ 1, 2, 3 ], [ 1, 3 ], [ 2 ], [ 2, 3 ], [ 3 ] ]
1
2
3
4
2
3
4
const arr = [1, 2, 3];
const fullAlignment = (arr) => {
const ans = [];
const deepFn = (currentArr, needArr) => {
if (needArr.length === 0) {
return;
}
needArr.forEach((item, i) => {
ans.push([...currentArr, item])
deepFn([...currentArr, item], needArr.slice(i + 1));
})
}
deepFn([], arr);
return ans;
}
console.log(fullAlignment(arr));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 82.深拷贝
const a = {
name: "aaa",
books: [
"a",
"a",
"a",
"a",
],
firends: [
{
name: "cc"
},
{
name: "ddd"
}
],
born: new Date(),
reg: /23/g,
symbol: Symbol(1),
}
const b = deepClone(a);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const a = {
name: "aaa",
books: [
"a",
"a",
"a",
"a",
],
firends: [
{
name: "cc"
},
{
name: "ddd"
}
],
born: new Date(),
reg: /23/g,
symbol: Symbol(1),
}
const deepClone = (obj) => {
if (typeof obj !== "object" && obj !== null) {
return obj;
} else if (Array.isArray(obj)) {
return obj.map(deepClone);
} else if (obj instanceof Date) {
return new Date(obj);
} else if (obj instanceof RegExp) {
return new RegExp(obj);
} else {
return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, deepClone(v)]));
}
}
const b = deepClone(a);
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
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
# 83.this指向
const obj = {
fn1: () => console.log(this),
fn2: function() {console.log(this)}
}
obj.fn1();
obj.fn2();
const x = new obj.fn1();
const y = new obj.fn2();
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
const obj = {
fn1: () => console.log(this),
fn2: function() {console.log(this)}
}
obj.fn1(); // global
obj.fn2(); // obj
// const x = new obj.fn1(); // error
// const y = new obj.fn2(); // fn2
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 84.树每层节点和
const tree = {
value: 2,
children: [
{ value: 6, children: [ { value: 1 } ] },
{ value: 3, children: [ { value: 2 }, { value: 3 }, { value: 4 } ] },
{ value: 5, children: [ { value: 7 }, { value: 8 } ] }
]
};
const res = layerSum(tree);
console.log(res);
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
const tree = {
value: 2,
children: [
{ value: 6, children: [ { value: 1 } ] },
{ value: 3, children: [ { value: 2 }, { value: 3 }, { value: 4 } ] },
{ value: 5, children: [ { value: 7 }, { value: 8 } ] }
]
};
function layerSum(root) {
const ans = [];
const deepFn = (nodes) => {
if (nodes.length === 0) return;
const nextNodes = [];
ans.push(nodes.reduce((pre, item) => {
if (item.children) {
nextNodes.push(...item.children);
}
return pre + item.value;
}, 0));
deepFn(nextNodes);
}
deepFn([root]);
return ans;
}
const res = layerSum(tree);
console.log(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
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
# 85. 有序数组原地去重
const arr = [1, 1, 1, 2, 2, 3, 3, 4, 4];
removeRepeat(arr);
console.log(arr); // [1, 2, 3, 4]
1
2
3
4
2
3
4
const arr = [1, 1, 1, 2, 2, 3, 3, 4, 4];
const removeRepeat = (arr) => {
for (let i = arr.length - 1; i > 0; i--) {
if (arr[i] === arr[i - 1]) {
arr.splice(i, 1);
}
}
}
removeRepeat(arr);
console.log(arr); // [1, 2, 3, 4]
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 86.叠词数量
const str = "abcdaaabbccccdddefgaaa";
console.log(getRepeatNum(str)); // { numOfRepeatChar: 5, lenOfSingleChar: 7 }
1
2
3
2
3
const str = "abcdaaabbccccdddefgaaa";
const getRepeatNum = (str) => {
const reg = /(.)\1+/g;
const ans = str.match(reg);
const numOfRepeatChar = ans ? ans.length : 0;
const lenOfSingleChar = str.replace(reg, "").length;
return {
numOfRepeatChar,
lenOfSingleChar,
};
}
console.log(getRepeatNum(str));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 87.sleep函数
- sleep函数
- 不阻塞线程
const sleep = (time) => new Promise(resolve => setTimeout(resolve, time));
const test = async () => {
console.log(0);
await sleep(1000);
console.log(1);
await sleep(1000);
console.log(2);
await sleep(1000);
console.log(3);
await sleep(1000);
console.log(4);
await sleep(1000);
}
test();
console.log("同步先行");
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
# 88.强密码正则
- 力扣 (opens new window)
- 密码有至少 8 个字符。
- 至少包含 一个小写英文 字母。
- 至少包含 一个大写英文 字母。
- 至少包含 一个数字 。
- 至少包含 一个特殊字符 。特殊字符为:"!@#$%^&*()-+" 中的一个。
- 它 不 包含 2 个连续相同的字符(比方说 "aab" 不符合该条件,但是 "aba" 符合该条件)。
var strongPasswordCheckerII = function(password) {};
console.log(strongPasswordCheckerII("IloveLe3tcode!")); // true
1
2
3
2
3
- 知识点:先行断言和先行否定查找 (opens new window)不占字符位,所以可以
叠加查找
并且不占用字符数量计算
var strongPasswordCheckerII = function(password) {
const reg = /^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[!@#$%^&*()\-+])(?!.*(.)\1).{8,}$/;
return reg.test(password);
};
console.log(strongPasswordCheckerII("IloveLe3tcode!")); // true
1
2
3
4
5
6
2
3
4
5
6
# 89.辗转相除法(gcd)
var gcdOfStrings = function(str1, str2) {};
console.log(gcdOfStrings("ABABAB", "ABAB")); // AB
1
2
3
2
3
- 辗转相除法(顺序不能变)
- 结束条件:小的等于0返回大的
- 持续返回
小的变大的
,大的除余小的
var gcdOfStrings = function(str1, str2) {
const gcd = (a, b) => b === 0 ? a : gcd(b, a % b);
if (`${str1}${str2}` !== `${str2}${str1}`) return "";
return str1.slice(0, gcd(str1.length, str2.length));
};
console.log(gcdOfStrings("ABABAB", "ABAB")); // AB
1
2
3
4
5
6
7
2
3
4
5
6
7
# 90.缓存函数
- 已经跑过的就直接拿答案
const fn = (n) => {
console.log("run");
return n + n;
};
const memoFn = memoization(fn);
memoFn(1);
memoFn(2);
memoFn(1);
memoFn(1);
memoFn(1);
// run
// run
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
const memoization = (fn) => {
const ansMap = new Map();
return (...args) => {
const key = JSON.stringify(args);
if (ansMap.has(key)) {
return ansMap.get(key);
} else {
const ans = fn(...args);
ansMap.set(key, ans);
return ans;
}
};
};
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 91.斐波那契
- 给数字n
- f(n) = f(n - 1) + f(n - 2)
- 垃圾解法,会爆栈
const fibonacci = (n, sum = 0) => {
return n === 0 ? sum : fibonacci(n - 1) + fibonacci(n - 2);
}
const n = fibonacci(10000000000);
console.log(n);
1
2
3
4
5
6
2
3
4
5
6
- 时间复杂度O(n)
- 空间复杂度O(n)
const fibonacci = (n) => {
const memo = new Map([
[0, 0],
[1, 1],
]);
const helperFn = (x) => {
if (memo.has(x)) {
return memo.get(x);
} else {
const ans = helperFn(x - 1) + helperFn(x - 2);
memo.set(x, ans);
return ans;
}
};
for (let i = 0; i < n; i++) {
helperFn(i);
}
return helperFn(n);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- 时间复杂度O(n)
- 空间复杂度O(n)
const fibonacci = (n) => {
const ansArr = Array(n + 1).fill();
ansArr[0] = 0;
ansArr[1] = 1;
if (n < 2) return n;
for (let i = 2; i <= n; i++) {
ansArr[i] = ansArr[i - 1] + ansArr[i - 2];
}
return ansArr[n];
};
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
- 时间复杂度O(n)
- 空间复杂度O(1)
const fibonacci = (n) => {
let first = 0;
let second = 1;
if (n < 2) return n;
// n === 2
let ans = first + second;
// n > 2
for (let i = 3; i <= n; i++) {
first = second;
second = ans;
ans = first + second;
}
return ans;
};
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
# 92.LazyMan
LazyMan("小黄"); // Hi this is 小黄!
LazyMan("小黄").sleep(10).eat("dinner"); // Hi this is 小黄! 等10s, Eat dinner
LazyMan("小黄").sleepFirst(5).eat("supper"); // // 等5s Hi this is 小黄! Eat supper
1
2
3
2
3
- 方案:
- sleep: 轮询
- firstSleep: 同步
// 方案1 同步控制
function LazyMan(name) {
let sleeping = false;
const me = {
name,
sayHello() {
Promise.resolve().then(() => {
console.log(`Hi! This is ${name}`);
});
return this;
},
sleep(seconds) {
sleeping = true;
setTimeout(() => {
sleeping = false;
}, seconds * 1000);
return this;
},
sleepFirst(seconds) {
sleeping = true;
const timeStart = Date.now();
// 减少同步等待计算量
const waitTime = seconds * 1000;
while (Date.now() - timeStart < waitTime) {}
sleeping = false;
return this;
},
eat(meal) {
if (sleeping) {
setTimeout(() => {
this.eat(meal);
}, 0);
} else {
setTimeout(() => {
console.log(`Eat ${meal}`);
}, 0);
}
return this;
}
};
setTimeout(() => {
me.sayHello();
}, 0);
return me;
}
// LazyMan('小黄'); // Hi this is 小黄!
// LazyMan('小黄').sleep(10).eat('dinner'); // Hi this is 小黄! 等10s, Eat dinner
LazyMan('小黄').sleepFirst(5).eat('supper'); // // 等5s Hi this is 小黄! Eat supper
// console.log('// 同步代码');
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
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
- 方案:
- sleep: 轮询
- firstSleep: 轮询
// 方案2 变量控制
function LazyMan (name) {
let sleeping = false;
let firstSleeping = false;
const me = {
sleep(time) {
sleeping = true;
setTimeout(() => {
sleeping = false;
}, time * 1000);
return this;
},
hello() {
setTimeout(() =>{
if (firstSleeping) {
setTimeout(() => this.hello());
} else {
console.log(`Hi this is ${name}!`);
}
});
return this;
},
eat(fruit) {
if (firstSleeping || sleeping) {
setTimeout(() => this.eat(fruit));
} else {
new Promise(res => res()).then(() =>console.log(`Eat ${fruit}`));
}
return this;
},
sleepFirst(time) {
// 方案2 变量
firstSleeping = true;
setTimeout(() => {
firstSleeping = false;
}, time * 1000);
return this;
}
}
setTimeout(() => me.hello());
return me;
}
// LazyMan("小黄"); // Hi this is 小黄!
// LazyMan("小黄").sleep(10).eat("dinner"); // Hi this is 小黄! 等10s, Eat dinner
LazyMan("小黄").sleepFirst(5).eat("supper"); // // 等5s Hi this is 小黄! Eat supper
console.log('// 同步代码');
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
- 方案:造Promise链
const LazyMan = (name) => {
const me = {
name,
queue: [],
hello() {
const next = () =>
new Promise((res) => {
console.log(` Hi this is ${this.name}!`);
res();
});
this.queue.push(next);
return this;
},
sleep(time) {
const next = () => new Promise((res) => setTimeout(res, time * 1000));
this.queue.push(next);
return this;
},
eat(something) {
const next = () =>
new Promise((res) => {
console.log(`Eat ${something}`);
res();
});
this.queue.push(next);
return this;
},
sleepFirst(time) {
const pre = () => new Promise((res) => setTimeout(res, time * 1000));
this.queue.unshift(pre);
return this;
},
start() {
setTimeout(() => {
this.queue.reduce((pre, next) => {
return pre.then(next);
}, Promise.resolve());
});
},
};
me.hello();
me.start();
return me;
};
// LazyMan("小黄"); // Hi this is 小黄!
LazyMan("小黄").sleep(10).eat("dinner"); // Hi this is 小黄! 等10s, Eat dinner
// LazyMan("小黄").sleepFirst(5).eat("supper"); // // 等5s Hi this is 小黄! Eat supper
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
- 方案:脱离Promise,造同步调用链
const LazyMan = (name) => {
const me = {
name,
queue: [],
sayHi() {
console.log(`Hi this is ${this.name}!`);
this.next();
},
eat(food) {
this.queue.push(() => {
console.log(`Eat ${food}`);
this.next();
});
return this;
},
sleep(time) {
this.queue.push(() => {
setTimeout(() => {
this.next();
}, time * 1000);
});
return this;
},
sleepFirst(time) {
this.queue.unshift(() => {
setTimeout(() => {
this.next();
}, time * 1000);
});
return this;
},
next() {
const fn = this.queue.shift();
fn && fn();
},
};
me.queue.push(me.sayHi.bind(me));
setTimeout(() => {
me.next();
}, 0);
return me;
};
// LazyMan("小黄"); // Hi this is 小黄!
LazyMan("小黄").sleep(10).eat("dinner"); // Hi this is 小黄! 等10s, Eat dinner
// LazyMan("小黄").sleepFirst(5).eat("supper"); // // 等5s Hi this is 小黄! Eat supper
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
- 51.id选择器问题
- 52.Worker作用
- 53.正则2
- 54.super对象
- 55.Symbol问题
- 56.首屏渲染时间
- 57.addEventListener移除
- 58.红绿灯
- 59.+优先级
- 60.节流事件
- 61.instanceof
- 62.this2
- 63.数组转对象
- 64.收集所有的DOM元素
- 65.多维数组全排列
- 66.交并集
- 67.手写继承
- 68.localStorate
- 69.手写JSONP实现
- 70.无符号计算
- 71.简易JSX compiler
- 72.链式函数
- 73.自定义事件
- 74.命名小驼峰转下划线
- 74.命名下划线转小驼峰
- 75.数组转树
- 76.限制请求数量
- 77.中文读法转换
- 78.去除字符串最少重复字符
- 79.归属数组
- 80.全排列
- 81.单数组全排列
- 82.深拷贝
- 83.this指向
- 84.树每层节点和
- 85. 有序数组原地去重
- 86.叠词数量
- 87.sleep函数
- 88.强密码正则
- 89.辗转相除法(gcd)
- 90.缓存函数
- 91.斐波那契
- 92.LazyMan