0%

fiber

fiber 本身是一个数据结构和执行单元

数据结构

tag fiber 节点类型
sibilings 兄弟节点
child 第一个子结点
return 父级

执行单元

1.React 向浏览器请求调度 2.浏览器空闲时会交给 React
3.React 判别是否空闲和未执行任务,否则交还给浏览器
raf

raf 浏览器绘画的 api,它要求浏览器在下一帧的时候调用回调函数渲染动画内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let dom = document.querySelector(".div");
let btn = document.querySelector(".btn");
let start;
let arr = [];
const process = () => {
dom.style.width = dom.offsetWidth + 1 + "px";
if (dom.style.width < 100) {
let time = +new Date();
arr.push({ time: time - start });
start = time;
requestAnimationFrame(process);
} else {
console.log("finish");
}
};

btn.onclick = () => {
dom.style.width = 0;
let time = +new Date();
start = time;
requestAnimationFrame(progress);
};

requestIdleCallBack

能够使代码块在浏览器分配低任务执行,以保证动画计时器正常执行
timeRemaining 是否有空闲时间
dealTime 是否还有执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const work = [
() => {
console.log("A1");
},
() => {
console.log("A2");
},
() => {
console.log("B1");
},
];

requestIdleCallBack(execJob);

const execJob = (time) => {
while (time.timeRemaining() > 0 && work.length > 0) {
runJob();
}
};

const runJob = () => {
let job = work.shift(); //取出,执行
job();
};

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
/**
* 为了确保兼容性,建议使用 requestAnimationFrame 的 polyfill 版本 raf
*/
const MOVE_ANIM_INTER = 1000
const raf = window.requestAnimationFrame;

/**
* 封装拖拽函数
* @param $ele 需要拖拽的元素
* @param adsorb { x = 20, y = 80 } 上下左右吸附的边距
*/
export default function draggable($ele, adsorb = { x: 20, y: 80 }) {
if (!$ele) {
throw new Error("必须是可拖拽元素");
}
// 开始时候的位置
let startX = 0;
let startY = 0;

// 移动过程中的 left 和 top,其实通过这俩参数,就能确定元素位置
let left = 0;
let top = 0;

// 屏幕的宽高
const cw = document.documentElement.clientWidth;
const ch = document.documentElement.clientHeight;

// 获取到元素自身的宽高
const { width, height } = $ele.getBoundingClientRect();

/**
* 开始拖拽的事件
*/
$ele.addEventListener(
"touchstart",
function (event) {
startX = event.targetTouches[0].pageX;
startY = event.targetTouches[0].pageY;

top = $ele.offsetTop;
left = $ele.offsetLeft;

event.preventDefault();
},
false
);

/**
* 拖拽过程中的事件
*/
$ele.addEventListener(
"touchmove",
function (event) {
// 偏移距离
const offsetX = event.targetTouches[0].pageX - startX;
const offsetY = event.targetTouches[0].pageY - startY;

$ele.style.top = `${top + offsetY}px`;
$ele.style.left = `${left + offsetX}px`;
$ele.style.right = "auto";
$ele.style.bottom = "auto";

event.preventDefault();
},
false
);

function touchDone(event) {
const dx = event.changedTouches[0].pageX - startX;
const dy = event.changedTouches[0].pageY - startY;

const ty = top + dy;
const tx = left + dx;

$ele.style.top = `${ty}px`;
$ele.style.left = `${tx}px`;
$ele.style.right = "auto";
$ele.style.bottom = "auto";

const adsorb_safe_x = cw - width - adsorb.x;
const adsorb_safe_y = ch - height - adsorb.y;

raf(() => {
console.log('raf')
let nx;
let ny = ty;

if (tx + width / 2 < cw / 2) {
nx = adsorb.x;
} else {
nx = adsorb_safe_x;
}

if (ty < adsorb.y) {
ny = adsorb.y;
} else if (ty > adsorb_safe_y) {
ny = adsorb_safe_y;
}

$ele.style.webkitTransition = `left ${MOVE_ANIM_INTER}ms ease-in-out, top ${MOVE_ANIM_INTER}ms ease-in-out`;
$ele.style.transition = `left ${MOVE_ANIM_INTER}ms ease-in-out, top ${MOVE_ANIM_INTER}ms ease-in-out`;

const onAnimationDone = () => {
$ele.style.webkitTransition = $ele.style.transition = "none";
$ele.removeEventListener("webkitTransitionEnd", onAnimationDone, false);
$ele.removeEventListener("transitionend", onAnimationDone, false);
};

$ele.addEventListener("webkitTransitionEnd", onAnimationDone, false);
$ele.addEventListener("transitionend", onAnimationDone, false);
$ele.style.top = `${ny}px`;
$ele.style.left = `${nx}px`;
});
}

$ele.addEventListener("touchend", touchDone, true);
$ele.addEventListener("touchcancel", touchDone, true);
}

command 命令

1
2
3
4
5
6
7

const { Command } = require('commander');
const program = new Command();
program.version('0.0.1');

program
.option('-j | --join','Join IMWeb now!')

错误处理

try catch

1.能捕获正常语法的错误
2.捕获不到语法错误
3.捕获不到异步错误 settimeout等

window.error

1.异步、正常语法错误可以捕获
2.资源错误、语法错误无法捕获

window.addEventListener

1.图片、css、js资源错误可以捕获
2.new Image 、fetch无法捕获

Promise 捕获

1.catch

1
2
3
promise错误都可以通过 
window.addEventListener('unhandlerejection',e=>{console.log(e)})

save restore

save保存上一次的状态,restore恢复上一次的状态

二叉堆

当节点下标为 n,它的左右孩子节点分别为 2n+1 2n+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
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
public class BinaryHeap {

/**上浮操作,对插入的节点进行上浮 小的上去
*
* @param arr
* @param length :表示二叉堆的长度
*/
public static int[] upAdjust(int arr[], int length){
//标记插入的节点
int child = length - 1;
//其父亲节点
int parent = (child - 1)/2;
//把插入的节点临时保存起来
int temp = arr[child];

//进行上浮
while (child > 0 && temp < arr[parent]) {
//其实不用进行每次都进行交换,单向赋值就可以了
//当temp找到正确的位置之后,我们再把temp的值赋给这个节点
arr[child] = arr[parent];
child = parent;
parent = (child - 1) / 2;
}
//退出循环代表找到正确的位置
arr[child] = temp;
return arr;
}

/**

*/

/**
* 下沉操作,执行删除操作相当于把最后
* * 一个元素赋给根元素之后,然后对根元素执行下沉操作
1.存放临时下沉元素 根据下标找到左孩
2.判断左右孩子大小,如果右的小让 child下标++
3.当临时下沉元素 <= 孩子节点,结束
4.父子替换,更新父下标为子,子下标为 2n+1
5.把最终的父级(下标)被赋值于临时下沉元素 arr[parent] = temp
* @param arr
* @param parent 要下沉元素的下标
* @param length 数组长度
*/
public static int[] downAdjust(int[] arr, int parent, int length) {
//临时保证要下沉的元素
int temp = arr[parent];
//定位左孩子节点位置
int child = 2 * parent + 1;
//开始下沉
while (child < length) {
// 如果右孩子节点比左孩子小,则定位到右孩子
if (child + 1 < length && arr[child] > arr[child + 1]) {
child++;
}
// 如果父节点比孩子节点小或等于,则下沉结束
if (temp <= arr[child])
break;
// 单向赋值
arr[parent] = arr[child];
// 此时的父成为了之前子的下标
parent = child;
// 定位左孩子节点位置
child = 2 * parent + 1;
}
arr[parent] = temp;
return arr;
}

/**
* 构建操作
*
* @param arr
*/
public static int[] buildHead(int[] arr,int length) {
//从最后一个非叶子节点开始下沉
for (int i = (length - 2) / 2; i >= 0; i--) {
arr = downAdjust(arr, i, length);
}
return 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
poolLimit 限制并发数
array 任务
iteratorFn 迭代回调 用于实现对每个任务项进行处理,该函数会返回一个 Promise 对象或异步函数。

// 循环任务 创建异步任务,返回Promise 进行保存新的异步任务
// 并发数小于实际任务数,进行并发控制
// 执行后,移除已完成的任务
// 保存执行中的任务
// 执行中的大于限制任务 就进行Race
async function asyncPool(poolLimit, array, iteratorFn) {
const ret = [];
const executing = [];
for (const item of array) {
const p = Promise.resolve().then(() => iteratorFn(item, array));
ret.push(p);

if (poolLimit <= array.length) {
const e = p.then(() => executing.splice(executing.indexOf(e), 1));
executing.push(e);
if (executing.length >= poolLimit) {
await Promise.race(executing);
}
}
}
return Promise.all(ret);
}

let timeout = i => new Promise(resolve => setTimeout(() => resolve(i), i));
await asyncPool(2, [1000, 3000, 2000], timeout);

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
function fetch(url) {
// 模拟接口请求
return new Promise((resolve) => {
setTimeout(() => {
resolve(url);
}, 1000);
});
}

/**
* 接口请求最大并发量控制
* @param { Array } urls 接口请求地址数组集合
* @param { Number } max 最大并发量
* @param { Function } callback 回调函数
*/
function maxRequestLimit(arr, max, callback) {
// 如果没有传入urls或max则不继续执行
if (!arr || !max) return;

// 当请求地址数组集合长度为0,则执行回调函数(如果有的话),并结束后续执行
if (arr.length === 0) {
if (callback) callback();
return;
}

let fetchArr = [], // 存储并发max的promise数组
i = 0;

// 1.fetch
// 2.执行并且放入存储队列
// 3.判别并行数量与任务最大限制 如果超过通过race执行第一个完成的
// 4.调用下函数自身
function toFetch() {
// 所有的请求都受理,则返回一个resolve
if (i === arr.length) return Promise.resolve();

// 取出第i个url, 放入fetch里面 , 每取一次i++
let one = fetch(arr[i++]);

//将当前的promise存入并发数组中
fetchArr.push(one);

// 当promise执行完毕后,从数组删除
one.then((res) => {
console.log(res);
fetchArr.splice(fetchArr.indexOf(one), 1);
});

let p = Promise.resolve();

// 当并行数量达到最大后, 用race比较 第一个完成的, 然后再调用一下函数自身。
if (fetchArr.length >= max) p = Promise.race(fetchArr);

return p.then(() => toFetch());
}

// fetchArr循环完后,现在fetchArr里面剩下的promise对象, 使用all等待所有的都完成之后执行callback
toFetch()
.then(() => Promise.all(fetchArr))
.then(() => callback());
}
maxRequestLimit([1, 2, 3, 4, 5, 7, 8, 9], 3, () => {
console.log("fetch end");
});

删除一个元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[1,2,3,4] 2 ,返回length
function deleteNum(arr,val){
let k = 0
for (let i =0;i<arr.length;i++){
if(arr[i]!=val){
arr[k++] = arr[i]
}
}
return k
}

[1,2,3,4] 2



二分法查找 注意区间[]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
输入: (nums = [-1, 0, 3, 5, 9, 12]), (target = 9);
输出: 4;

function search(nums, target) {
let l = 0,
r = nums.length - 1;
while (l <= r) {
let mid = (l + r) >> 1; // 00101 -> 0010
let isShort = nums[mid] < target; // target 右区间
l = isShort ? mid + 1 : l; // 在右区间 【middle+1,right】
r = isShort ? r : mid - 1; // 在左区间 【left,middle-1】
}
return -1;
}

有序数组平方排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[-4, -2, 0, 1, 2];
const sortedSquares = function (nums) {
let res = [];
for (let i = 0, j = nums.length - 1; i <= j; ) {
const left = Math.abs(nums[i]);
const right = Math.abs(nums[j]);
if (right > left) {
res.unshift(right * right);
j--;
} else {
res.unshift(left * left);
i++;
}
}
return res;
};

最小连续数组满足条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let minSubArrayLen = function (target, nums) {
const len = nums.length;
let l = (r = sum = 0),
res = len + 1; // 子数组最大不会超过自身
while (r < len) {
sum += nums[r++];
// 窗口滑动
while (sum >= target) {
// r始终为开区间 [l, r)
res = res < r - l ? res : r - l;
sum -= nums[l++];
}
}
return res > len ? 0 : res;
};
let result = minSubArrayLen(7, [2, 3, 1, 2, 4, 3]);

矩阵

1

字符串反转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let str = "My name is yhq";
const reverseStr = (s) => {
let left = 0;
let right = s.length - 1;
let quene = [];
let word = "";
while (s.charAt(left) === " ") left++;
while (s.charAt(right) === " ") right--;
while (left <= right) {
let char = s.charAt(left);
if (char === " " && word) {
quene.unshift(word);
word = "";
} else if (char !== " ") {
word += char;
}
left++;
}
quene.unshift(word);
return quene.join(" ");
};
reverseStr(str);

括号

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
class Stack {
constructor() {
this.items = [];
}
push(value) {
this.items.push(value);
}
pop(value) {
if (!this.isEmpty()) {
this.items.pop(value);
}
}
isEmpty() {
return this.items?.length === 0;
}
size() {
return this.items.length;
}
}

const isPairing = (str) => {
const stack = new Stack();
for (let i of str) {
if (i === "(") {
stack.push(i);
} else if (i === ")") {
if (stack.isEmpty()) {
return false;
} else {
stack.pop();
}
}
}
return stack.size() === 0;
};

s = “abaccdeff” 字符出现一次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const recordFirstUnit = (str) => {
if (!str) return "";
const mapData = new Map();
for (let i of str) {
if (mapData.has(i)) {
mapData.set(i, mapData.get(i) + 1);
} else {
mapData.set(i, 1);
}
}
for (let j of mapData.keys()) {
if (mapData.get(j) === 1) {
return j;
}
}

return " ";
};

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
/**
* 功能:单链表的插入、删除、查找
* 【插入】:插入到指定元素后方
* 1、查找该元素是否存在?
* 2、没有找到返回 -1
* 3、找到进行创建结点并插入链表。
*
* 【查找】:按值查找/按索引查找
* 1、判断当前结点是否等于null,且是否等于给定值?
* 2、判断是否可以找到该值?
* 3、没有找到返回 -1;
* 4、找到该值返回结点;
*
* 【删除】:按值删除
* 1、判断是否找到该值?
* 2、找到记录前结点,进行删除;
* 3、找不到直接返回-1;
*
*
* 数据域用于存放数据
* 指针域用于存放下一结点地址
*
* 通过数据库访问结点的数据
* 通过指针域访问当前结点的下一结点
* head [1,2]->[3,4]->[5,null]
*/

//定义结点
class Node {
constructor(data) {
this.data = data;
this.next = null;
}
}

//定义链表
class LinkList {
constructor() {
//初始化头结点
this.head = new Node("head");
}

//根据 value 查找结点
findByValue = (value) => {
let currentNode = this.head;
while (currentNode !== null && currentNode.data !== value) {
// 一直查找直到找到
currentNode = currentNode.next;
}
//判断该结点是否找到
console.log(currentNode);
return currentNode === null ? -1 : currentNode;
};


//根据 index 查找结点
findByIndex = (index) => {
let pos = 0;
let currentNode = this.head;
while (currentNode !== null && pos !== index) {
currentNode = currentNode.next;
pos++;
}
//判断是否找到该索引
console.log(currentNode);
return currentNode === null ? -1 : currentNode;
};


//插入元素(指定元素向后插入)
insert = (value, element) => {
//先查找该元素
let currentNode = this.findByValue(element);
//如果没有找到
if (currentNode == -1) {
console.log("未找到插入位置!");
return;
}
let newNode = new Node(value);
// currentNode.next 为元素的临时指针
// newNode.next 插入元素的下一个
// 插入元素的下一个指向 存放元素的临时指针
newNode.next = currentNode.next;
// 元素的下一指针指向新元素
currentNode.next = newNode;

};



//根据值删除结点
delete = (value) => {
let currentNode = this.head;
//保留删除节点的前一节点 1->2->3->4
let preNode = null;
while (currentNode !== null && currentNode.data !== value) {
// 删除结点的前一结点
preNode = currentNode;
// 删除结点
currentNode = currentNode.next;
}
if (currentNode == null) return -1;
// 删除结点的前一结点指向删除结点的下一结点
preNode.next = currentNode.next;
};

//遍历所有结点
print = () => {
let currentNode = this.head;
//如果结点不为空
while (currentNode !== null) {
console.log(currentNode.data);
currentNode = currentNode.next;
}
};
}

//测试
const list = new LinkList();
list.insert("xiao", "head");
list.insert("lu", "xiao");
list.insert("ni", "head");
list.insert("hellow", "head");
list.print();
console.log("-------------删除元素------------");
list.delete("ni");
list.delete("xiao");
list.print();
console.log("-------------按值查找------------");
list.findByValue("lu");
console.log("-------------按索引查找------------");
list.print();

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment