JS手写

手写数组转树

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
// 例如将 input 转成output的形式
let input = [
{
id: 1,
val: "学校",
parentId: null,
},
{
id: 2,
val: "班级1",
parentId: 1,
},
{
id: 3,
val: "班级2",
parentId: 1,
},
{
id: 4,
val: "学生1",
parentId: 2,
},
{
id: 5,
val: "学生2",
parentId: 2,
},
{
id: 6,
val: "学生3",
parentId: 3,
},
];

let output = {
id: 1,
val: "学校",
children: [
{
id: 2,
val: "班级1",
children: [
{
id: 4,
val: "学生1",
children: [],
},
{
id: 5,
val: "学生2",
children: [],
},
],
},
{
id: 3,
val: "班级2",
children: [
{
id: 6,
val: "学生3",
children: [],
},
],
},
],
};
1
2
3
4
5
6
7
8
function arrToTree(arr) {
return arr.filter((item) => {
item.children = arr.filter((i) => {
return item.id === i.parentId;
});
return !item.parentId;
});
}
1
2
3
4
5
6
7
8
9
function arrToTree(arr, parentId) {
let parentArr = arr.filter((item) =>
parentId === undefined ? item.parentId === null : item.parentId === parentId
);
return parentArr.map((i) => {
i.children = arrToTree(arr, i.id);
return i;
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function getChildren(arr, result, parentId) {
for (let item of arr) {
if (item.parentId === parentId) {
const newItem = { ...item, children: [] };
result.push(newItem);
getChildren(arr, newItem.children, item.id);
}
}
}
function arrToTree(arr, parentId) {
const result = [];
getChildren(arr, result, parentId);
return result;
}
1
2
3
4
5
6
7
8
9
function arrToTree(arr) {
let map = new Map();
for (let item of arr) {
item.children = [];
map.set(item.id, item);
map.has(item.parentId) && map.get(item.parentId).children.push(item);
}
return map.get(1);
}
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 arrayToTree(array) {
let root = array[0];
array.shift();
let tree = {
id: root.id,
val: root.val,
children: array.length > 0 ? toTree(root.id, array) : [],
};
return tree;
}

function toTree(parenId, array) {
let children = [];
let len = array.length;
for (let i = 0; i < len; i++) {
let node = array[i];
if (node.parentId === parenId) {
children.push({
id: node.id,
val: node.val,
children: toTree(node.id, array),
});
}
}
return children;
}

console.log(arrayToTree(input));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 写法二
function getTreeList(rootList, id, list) {
for (let item of rootList) {
if (item.parentid === id) {
list.push(item);
}
}

for (let i of list) {
i.children = [];
getTreeList(rootList, i.id, i.children);
// 去除children为[]的情况
if (i.children.length === 0) {
delete i.children;
}
}
return list;
}
const res = getTreeList(input, null, []);

手写 Promise/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
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// 定义状态
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

class MyPromise {
constructor(executor) {
// 捕获执行器的错误
try {
// 立即执行执行器
executor(this.resolve, this.reject);
} catch (error) {
this.reject(error);
}
// promise状态
this.status = PENDING; // 默认值
// 成功后的值
this.value = undefined;
// 失败后的原因
this.reason = undefined;
// 成功回调
this.successCallback = [];
// 失败回调
this.failCallback = [];
}

// 添加resolve
resolve = (value) => {
// 由状态修改后无法再次修改
// 如果状态不是等待 就阻止程序向下执行
if (this.status !== PENDING) return;

// 将状态更改为成功
this.status = FULFILLED;
// 保存成功后的值
this.value = value;
// 成功回调是否存在,存在就调用
// this.successCallback && this.successCallback(this.value)
this.successCallback.forEach((fn) => fn());
};
// 添加reject
reject = (reason) => {
// 如果状态不是等待 就阻止程序向下执行
if (this.status !== PENDING) return;
// 将状态更改为失败
this.status = REJECTED;
// 保存失败后的原因
this.reason = reason;
// 失败回调是否存在,存在就调用
// this.failCallback && this.failCallback(this.reason)
this.failCallback.forEach((fn) => fn());
};

// then方法内部就是判断状态,成功状态调用成功函数,失败状态调用失败函数
// then成功回调的参数是成功后的值,失败回调的参数是返回的原因

then = (successCallback, failCallback) => {
// then链式传递
successCallback =
typeof successCallback === "function"
? successCallback
: (value) => value;
failCallback =
typeof failCallback === "function"
? failCallback
: (reason) => {
throw reason;
};
// then方法每次都要返回一个promise
let promise2 = new MyPromise((resolve, reject) => {
// 判断状态
if (this.status === FULFILLED) {
// 将代码用setTimeout包裹变成异步代码,为了能拿到promise2
setTimeout(() => {
// 捕获错误
try {
let x = successCallback(this.value);
// 判断x的值是普通值还是promise对象
// 如果是普通值,直接调用resolve
// 如果是promise, 查看promise对象返回的结果,根据结果,绝对调用resolve还是reject
// 把返回值传递给下一个回调
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
} else if (this.status === REJECTED) {
// 将代码用setTimeout包裹变成异步代码,为了能拿到promise2
setTimeout(() => {
// 捕获错误
try {
let x = failCallback(this.value);
// 判断x的值是普通值还是promise对象
// 如果是普通值,直接调用resolve
// 如果是promise, 查看promise对象返回的结果,根据结果,绝对调用resolve还是reject
// 把返回值传递给下一个回调
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
} else {
// 状态是pending,是存在异步情况
// 将成功回调和失败回调存储起来
this.successCallback.push(() => {
setTimeout(() => {
// 捕获错误
try {
let x = successCallback(this.value);
// 判断x的值是普通值还是promise对象
// 如果是普通值,直接调用resolve
// 如果是promise, 查看promise对象返回的结果,根据结果,绝对调用resolve还是reject
// 把返回值传递给下一个回调
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
this.failCallback.push(() => {
setTimeout(() => {
// 捕获错误
try {
let x = failCallback(this.value);
// 判断x的值是普通值还是promise对象
// 如果是普通值,直接调用resolve
// 如果是promise, 查看promise对象返回的结果,根据结果,绝对调用resolve还是reject
// 把返回值传递给下一个回调
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
}
});

return promise2;
};
// 捕获错误
catch(failCallback) {
return this.then(undefined, failCallback);
}

// 返回 then的链式调用
finally = (callback) => {
// 使用then保证链式调用
return this.then(
(value) => {
// 使用resolve保证返回promise对象
return MyPromise.resolve(callback()).then(() => value);
},
(reason) => {
return MyPromise.resolve(callback()).then(() => {
throw reason;
});
}
);
};

static all(array) {
// 设置返回的数组
let result = [];
// 设置计数器
let index = 0;
return new MyPromise((resolve, reject) => {
for (let i = 0; i < array.length; i++) {
let current = array[i];
if (current instanceof MyPromise) {
// 是promise对象
current.then(
(value) => addData(i, value),
(reason) => reject(reason)
);
} else {
// 普通值
addData(i, array[i]);
}
}

function addData(key, value) {
// 保证位置是按顺序的
result[key] = value;
// 跑一个算一个
index++;
// 计数器和数组长度相等说明都跑完了
if (index === array.length) {
resolve(result);
}
}
});
}
static resolve(value) {
if (value instanceof MyPromise) return value;
return new MyPromise((resolve) => resolve(value));
}
}

const resolvePromise = (promise2, x, resolve, reject) => {
if (promise2 === x) {
// 如果自我调用就报错,并阻止继续执行
return reject(
new TypeError("Chaining cycle detected for promise #<Promise>")
);
}
if (x instanceof MyPromise) {
// x 是否是MyPromise的实例对象,是就是promise对象
x.then(resolve, reject);
} else {
// 普通值
resolve(x);
}
};

手写去重

1
2
let arr = [];
Array.form(new Set(arr));

手写防抖

顾名思义,防止抖动,手抖多点了几次,怎么办,只算最后一次.
debounce

手写节流

顾名思义,一直在流,但是为了节约,隔一段时间流一次.
throttle

手写多维数组扁平化

手写 redux

手写 useState,useEffect,useReducer

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
let state = [];
let setters = [];
let stateIndex = 0;

function createSetter(index) {
return function (newState) {
state[index] = newState;
render();
};
}

function useState(initState) {
state[stateIndex] = state[stateIndex] ? state[stateIndex] : initState;
setters.push(createSetter(stateIndex));
stateIndex++;
// 状态
let value = state[stateIndex];
// 方法
let setter = setters[stateIndex];

return [value, setter];
}

// 上一次的依赖值
let prevDepsAry = [];
// 这个声明用于多个effect的情况
let effectIndex = 0;

function useEffect(callback, depsAry) {
// 判断callback是否为函数
if (Object.prototype.toString.call(callback) !== "[object Function]")
throw new Error("useEffect接收一个函数");
// 判断depsAry是否传递
if (typeof depsAry === "undefined") {
// 没有传递
callback();
} else {
// 判断depsAry是不是数组
if (Object.prototype.toString.call(depsAry) !== "[object Array]")
throw new Error("useEffect依赖必须是数组");
// 获取上一次的值,如果有就取,没有不取
let prevDeps = prevDepsAry[effectIndex];
// 判断当前依赖值与上一次依赖,是否有变化,有才调用callback
let hasChanged = prevDeps
? depsAry.every((dep, index) => dep === prevDeps[index]) === false
: true;
// 判断值是否有变化
if (hasChanged) {
callback();
}
// 比完之后要同步依赖值,保证依赖值最新
prevDepsAry[effectIndex] = depsAry;
// 多个useEffect时标记增加,记得在render时归零
effectIndex++;
}
}

function useReducer(reducer, initState) {
const [state, setState] = useState(initState);
// useReducer内部返回dispatch方法
function dispatch(action) {
// dispatch内部调用reducer返回最新的state
const newState = reducer(state, action);
setState(newState);
}
return [state, dispatch];
}

function App() {
const [count, dispatch] = useReducer(render, 0);

function reducer(state, action) {
switch (action.type) {
case "increment":
return state + 1;
break;
case "decrement":
return state - 1;
default:
return state;
}
}

return (
<div>
{count}
<button onClick={() => dispatch({ type: "increment" })}>add</button>
<button onClick={() => dispatch({ type: "decrement" })}>delete</button>
</div>
);
}

function render() {
stateIndex = 0;
effectIndex = 0;

ReactDOM.render(<App />, document.getElementById("root"));
}

手写 Vue

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
const Observer = function (data) {
// 循环修改为每个属性添加get set
for (let key in data) {
defineReactive(data, key);
}
};

const defineReactive = function (obj, key) {
// 局部变量dep,用于get set内部调用
const dep = new Dep();
// 获取当前值
let val = obj[key];
Object.defineProperty(obj, key, {
// 设置当前描述属性为可被循环
enumerable: true,
// 设置当前描述属性可被修改
configurable: true,
get() {
console.log("in get");
// 调用依赖收集器中的addSub,用于收集当前属性与Watcher中的依赖关系
dep.depend();
return val;
},
set(newVal) {
if (newVal === val) {
return;
}
val = newVal;
// 当值发生变更时,通知依赖收集器,更新每个需要更新的Watcher,
// 这里每个需要更新通过什么断定?dep.subs
dep.notify();
},
});
};

const observe = function (data) {
return new Observer(data);
};

const Vue = function (options) {
const self = this;
// 将data赋值给this._data,源码这部分用的Proxy所以我们用最简单的方式临时实现
if (options && typeof options.data === "function") {
this._data = options.data.apply(this);
}
// 挂载函数
this.mount = function () {
new Watcher(self, self.render);
};
// 渲染函数
this.render = function () {
with (self) {
_data.text;
}
};
// 监听this._data
observe(this._data);
};

const Watcher = function (vm, fn) {
const self = this;
this.vm = vm;
// 将当前Dep.target指向自己
Dep.target = this;
// 向Dep方法添加当前Wathcer
this.addDep = function (dep) {
dep.addSub(self);
};
// 更新方法,用于触发vm._render
this.update = function () {
console.log("in watcher update");
fn();
};
// 这里会首次调用vm._render,从而触发text的get
// 从而将当前的Wathcer与Dep关联起来
this.value = fn();
// 这里清空了Dep.target,为了防止notify触发时,不停的绑定Watcher与Dep,
// 造成代码死循环
Dep.target = null;
};

const Dep = function () {
const self = this;
// 收集目标
this.target = null;
// 存储收集器中需要通知的Watcher
this.subs = [];
// 当有目标时,绑定Dep与Wathcer的关系
this.depend = function () {
if (Dep.target) {
self.addSub(Dep.target);
}
};
// 为当前收集器添加Watcher
this.addSub = function (watcher) {
self.subs.push(watcher);
};
// 通知收集器中所的所有Wathcer,调用其update方法
this.notify = function () {
for (let i = 0; i < self.subs.length; i += 1) {
self.subs[i].update();
}
};
};

const vue = new Vue({
data() {
return {
text: "hello world",
};
},
});

vue.mount(); // in get
vue._data.text = "123"; // in watcher update /n in get