Skip to content

Commit 5fcc37a

Browse files
feat: update
1 parent f1c0dc7 commit 5fcc37a

File tree

2 files changed

+201
-0
lines changed

2 files changed

+201
-0
lines changed

docs/jsCode/LazyMan.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ class _LazyMan {
111111
// 每次调用时清楚timer,上一次设置的执行taskQueue就不会运行。
112112
// 重新设置timer,会在下一次调用完后进入执行。
113113
// 当所有调用结束后,就会顺利执行taskQueue队列里的事件
114+
// 因为要收集好所有任务才开始执行,所以我们要用 setTimeout 构造一个异步的宏任务,确保任务的执行在同步代码后执行。
114115
next() {
115116
clearTimeout(this.timer);
116117
this.timer = setTimeout(async () => {
@@ -160,7 +161,28 @@ function LazyMan(name) {
160161

161162
考察知识点:闭包,事件轮询机制,链式调用,队列
162163

164+
从题目可以看出:
165+
166+
1. 同步的代码调用,但是是异步的结果输出。
167+
2. 没有结束时刻,怎么知道调用哪个函数的时候应该输出
168+
169+
### 思路分析
170+
171+
1. LazyMan(“Hank”)调用,而不是 new LazyMan(“Hank”)创建 => 工厂方法返回 new 对象
172+
2. 链式调用实现 => 每次调用返回 this
173+
3. sleep 需要等待 10s => setTimeout 实现 sleep
174+
4. setTimeout 会放到事件列表中排队,继续执行后面的代码,但是题目中 sleep 需要阻塞后续操作。 => 考虑将 sleep 封装成 promise,使用 async/await 等待 sleep,实现阻塞。
175+
5. sleepFirst 每次在最开始执行,考虑将 sleepFirst 插入到事件第一个执行。
176+
6. 不到最后一刻,是不知道调用顺序是什么的,所以需要先是使用一个队列把时间收集起来,最后再统一执行
177+
178+
179+
因此,首先我们需要 taskQueue 记录事件列表,直到调用完成后再执行 taskQueue 里面的事件。怎么实现调用完成后才开始执行 taskQueue 的事件呢?
180+
答案:setTimeout 机制。setTimeout(function(){xxx},0)不是立马执行,这是因为 js 是单线程的,有一个事件队列机制,setTimeout 和 setInterval 的回调会插入到延迟时间塞入事件队列中,排队执行。
181+
163182
### 参考
164183

165184
- [如何实现一个 LazyMan?](https://zhuanlan.zhihu.com/p/22387417)
166185
- [LazyMan 的 ES6 实现](https://segmentfault.com/a/1190000022958490)
186+
- [多种方式实现 LazyMan](https://xie.infoq.cn/article/818cefbaf4ec318dda0e8eb2a)
187+
- [编程题:实现一个 LazyMan 方法](https://developer.51cto.com/article/708855.html)
188+
- [字节跳动面试:实现一个 LazyMan 函数](https://blog.csdn.net/qq_39261142/article/details/110425286)

docs/jsCode/沙箱.md

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
## 什么是沙箱
2+
3+
### 沙箱的应用场景
4+
5+
### 沙箱的实现方式
6+
7+
## 简单实现一个沙箱
8+
9+
### 基于快照实现一个沙箱
10+
11+
支持单应用的代理沙箱-极简版
12+
13+
```js
14+
class SnapshotSandbox {
15+
constructor() {
16+
this.proxy = window;
17+
this.sandboxSnapshot = {}; // 记录在沙箱上的所有属性快照
18+
this.windowSnapshot = {}; // 存储window上的所有属性快照
19+
this.active();
20+
}
21+
// 激活沙箱,在沙箱里进行操作
22+
active() {
23+
for (const prop in window) {
24+
if (window.hasOwnProperty(prop)) {
25+
// 激活时,先把window上的属性保存在 windowSnapshot
26+
this.windowSnapshot[prop] = window[prop];
27+
}
28+
}
29+
// 再把沙箱之前的属性重新赋值给 window
30+
Object.keys(this.sandboxSnapshot).forEach((p) => {
31+
window[p] = this.sandboxSnapshot[p];
32+
});
33+
}
34+
//失活
35+
inActive() {
36+
for (const prop in window) {
37+
if (window.hasOwnProperty(prop)) {
38+
// 比较现在的window 和之前的 window对比有啥区别
39+
if (window[prop] !== this.windowSnapshot[prop]) {
40+
// 如果不一样,就说明有变化,需要保存变化
41+
// 把沙箱的属性保存在sandboxSnapshot
42+
this.sandboxSnapshot[prop] = window[prop];
43+
// 需要将window上之间的属性 windowSnapshot 再赋值给window
44+
window[prop] = this.windowSnapshot[prop];
45+
}
46+
}
47+
}
48+
}
49+
}
50+
51+
let sandBox = new SnapshotSandbox();
52+
// 应用的运行从开始到结束,切换后不会影响全局
53+
((window) => {
54+
window.a = 1;
55+
window.b = 2;
56+
console.log(window.a, window.b); // 1, 2
57+
// 失活
58+
sandBox.inActive();
59+
console.log(window.a, window.b); // undefined undefined
60+
// 激活还原
61+
sandBox.active();
62+
console.log(window.a, window.b); // 1, 2
63+
})(sandBox.proxy); // sandBox.proxy就是window
64+
```
65+
66+
存在的问题
67+
68+
### 基于 proxy 实现一个沙箱
69+
70+
```js
71+
class LegacySandBox {
72+
addedPropsMapInSandbox = new Map();
73+
modifiedPropsOriginalValueMapInSandbox = new Map();
74+
currentUpdatedPropsValueMap = new Map();
75+
proxyWindow;
76+
setWindowProp(prop, value, toDelete = false) {
77+
if (value === undefined && toDelete) {
78+
delete window[prop];
79+
} else {
80+
window[prop] = value;
81+
}
82+
}
83+
active() {
84+
this.currentUpdatedPropsValueMap.forEach((value, prop) => this.setWindowProp(prop, value));
85+
}
86+
inactive() {
87+
this.modifiedPropsOriginalValueMapInSandbox.forEach((value, prop) =>
88+
this.setWindowProp(prop, value)
89+
);
90+
this.addedPropsMapInSandbox.forEach((_, prop) => this.setWindowProp(prop, undefined, true));
91+
}
92+
constructor() {
93+
const fakeWindow = Object.create(null);
94+
this.proxyWindow = new Proxy(fakeWindow, {
95+
set: (target, prop, value, receiver) => {
96+
const originalVal = window[prop];
97+
if (!window.hasOwnProperty(prop)) {
98+
this.addedPropsMapInSandbox.set(prop, value);
99+
} else if (!this.modifiedPropsOriginalValueMapInSandbox.has(prop)) {
100+
this.modifiedPropsOriginalValueMapInSandbox.set(prop, originalVal);
101+
}
102+
this.currentUpdatedPropsValueMap.set(prop, value);
103+
window[prop] = value;
104+
},
105+
get: (target, prop, receiver) => {
106+
return target[prop];
107+
},
108+
});
109+
}
110+
}
111+
// 验证:
112+
let legacySandBox = new LegacySandBox();
113+
legacySandBox.active();
114+
legacySandBox.proxyWindow.city = 'Beijing';
115+
console.log('window.city-01:', window.city);
116+
legacySandBox.inactive();
117+
console.log('window.city-02:', window.city);
118+
legacySandBox.active();
119+
console.log('window.city-03:', window.city);
120+
legacySandBox.inactive();
121+
// 输出:
122+
// window.city-01: Beijing
123+
// window.city-02: undefined
124+
// window.city-03: Beijing
125+
```
126+
127+
支持多应用的代理沙箱-极简版
128+
129+
```js
130+
class ProxySandBox {
131+
constructor() {
132+
const fakeWindow = Object.create(null);
133+
this.proxyWindow = new Proxy(fakeWindow, {
134+
set: (target, prop, value, receiver) => {
135+
if (this.isRunning) {
136+
target[prop] = value;
137+
}
138+
},
139+
get: (target, prop, receiver) => {
140+
return prop in target ? target[prop] : window[prop];
141+
},
142+
});
143+
}
144+
proxyWindow;
145+
isRunning = false;
146+
active() {
147+
this.isRunning = true;
148+
}
149+
inactive() {
150+
this.isRunning = false;
151+
}
152+
}
153+
// 验证:
154+
let proxySandBox1 = new ProxySandBox();
155+
let proxySandBox2 = new ProxySandBox();
156+
proxySandBox1.active();
157+
proxySandBox2.active();
158+
proxySandBox1.proxyWindow.city = 'Beijing';
159+
proxySandBox2.proxyWindow.city = 'Shanghai';
160+
console.log('active:proxySandBox1:window.city:', proxySandBox1.proxyWindow.city);
161+
console.log('active:proxySandBox2:window.city:', proxySandBox2.proxyWindow.city);
162+
console.log('window:window.city:', window.city);
163+
proxySandBox1.inactive();
164+
proxySandBox2.inactive();
165+
console.log('inactive:proxySandBox1:window.city:', proxySandBox1.proxyWindow.city);
166+
console.log('inactive:proxySandBox2:window.city:', proxySandBox2.proxyWindow.city);
167+
console.log('window:window.city:', window.city);
168+
// 输出:
169+
// active:proxySandBox1:window.city: Beijing
170+
// active:proxySandBox2:window.city: Shanghai
171+
// window:window.city: undefined
172+
// inactive:proxySandBox1:window.city: Beijing
173+
// inactive:proxySandBox2:window.city: Shanghai
174+
// window:window.city: undefined
175+
```
176+
177+
## 参考
178+
179+
- [微前端 01 : 乾坤的 Js 隔离机制(快照沙箱、两种代理沙箱)](https://www.scanonly.com/article/298077)

0 commit comments

Comments
 (0)