冒泡排序是最慢的排序算法之一,也是最容易实现的排序算法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
66function CArray(numElements) {
this.dataStore = [];
this.pos = 0;
this.numElements = numElements;
this.insert = insert;
this.toString = toString;
this.clear = clear;
this.setData = setData;
this.swap = swap;
this.bubbleSort = bubbleSort;
for (let i = 0; i < numElements; ++i) {
this.dataStore[i] = i;
}
function setData() {
for (let i = 0; i < this.numElements; ++i){
this.dataStore[i] = Math.floor(Math.random() * (this.numElements + 1));
}
}
function clear(){
for(let i = 0; i<this.dataStore.length; ++i){
this.dataStore[i] = 0;
}
}
function insert(element){
this.dataStore[this.pos++] = element;
}
function toString(){
let str = '';
for(let i = 0; i<this.dataStore.length; ++i){
str += this.dataStore[i] + ' ';
// 每逢十个换行
if(i > 0 & i % 10 == 0){
str+='\n';
}
}
return str;
}
function swap(arr, index1, index2){
let temp = arr[index1];
arr[index1] = arr[index2];
arr[index2] = temp;
}
function bubbleSort() {
let numElements = this.dataStore.length;
let temp;
// 外循环用于遍历数组中的每一项元素
for (let outer = numElements; outer >= 2; --outer) {
// 内循环用于比较元素
for (let inner = 0; inner <= outer - 1; ++inner) {
if (this.dataStore[inner] > this.dataStore[inner + 1]) {
swap(this.dataStore, inner, inner + 1);
// console.log(this.dataStore)
}
}
console.log(this.toString())
}
}
}
// 测试代码
let numElements = 10;
let myNums = new CArray(numElements);
myNums.setData();
console.log(myNums.toString());
myNums.bubbleSort();
console.log(myNums.toString());
算法排序篇——数组测试平台
准备一个数组测试平台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
48function CArray(numElements) {
this.dataStore = [];
this.pos = 0;
this.numElements = numElements;
this.insert = insert;
this.toString = toString;
this.clear = clear;
this.setData = setData;
this.swap = swap;
for (let i = 0; i < numElements; ++i) {
this.dataStore[i] = i;
}
function setData() {
for (let i = 0; i < this.numElements; ++i){
this.dataStore[i] = Math.floor(Math.random() * (this.numElements + 1));
}
}
function clear(){
for(let i = 0; i<this.dataStore.length; ++i){
this.dataStore[i] = 0;
}
}
function insert(element){
this.dataStore[this.pos++] = element;
}
function toString(){
let str = '';
for(let i = 0; i<this.dataStore.length; ++i){
str += this.dataStore[i] + ' ';
// 每逢十个换行
if(i > 0 & i % 10 == 0){
str+='\n';
}
}
return str;
}
function swap(arr, index1, index2){
let temp = arr[index1];
arr[index1] = arr[index2];
arr[index2] = temp;
}
}
// 测试代码
let numElements = 100;
let myNums = new CArray(numElements);
myNums.setData();
console.log(myNums.toString())
从周报中获得什么
需求:进入详情页后,返回之前的列表页,需要自动定位到上一次浏览的位置
这里的解决方法是:将详情页独立为一个浮层,通过URL的参数来唤起这个浮层也就是详情页,点击返回时关闭浮层,整个过程背后都是列表页,这就决定了详情页(浮层)不参与路由
问题:当用户退出当前账户后重新进入时,购物车的数据被清空了
分析:现在购物车数据的存储一般都是利用session,这就涉及到session的生命周期
当用户退出登录重新登录时,如果cookies没有清除,那么还可以通过sessionId获取用户存储在服务器端的session数据
如果cookies被清除了,也只是清除了存储在其中的sessionId,session对象还不会被马上清除
那session对象什么时候被清除呢?
服务器不知道浏览器已经将sessionId清除了,相当于这段session已经失效了,因此它会给session设置一个过期日期,逾期就将session清除
回到问题,也就是其实就是用户退出后,还将cookies给清除了,那这样自然就无法取到存储在session的数据了
关于react性能优化的几个点
- 开启render插件,避免无关节点的渲染
- 将组件分割得更细,让组件的颗粒度更小,这样当数据更新,组件重新渲染的时候,就可以减少渲染的节点
数据结构篇——栈
栈是一种高效的数据结构,特点是只能在栈顶进行添加或删除操作,具有后入先出的特点,任何不在栈顶的元素都无法被访问。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
42function Stack(){
this.dataStore = [];
this.top = 0; // 记录栈顶的位置
this.push = push;
this.pop = pop;
this.peek = peek;
this.clear = clear;
this.length = length;
}
function push(element){
this.dataStore[this.top++] = element;
}
function pop(){ // 返回并删除栈顶元素
return this.dataStore[--this.top];
}
function peek(){ // 返回栈顶元素
return this.dataStore[this.top-1];
}
function length(){
return this.top;
}
function clear(){
this.top = 0;
}
// 测试代码
let s = new Stack();
s.push('a');
s.push('b');
s.push('c');
console.log(s.length())
console.log(s.peek())
s.push('d');
console.log(s.peek());
s.clear();
console.log(s.length());
console.log(s.peek());
数据结构篇——列表
列表是一种最自然的数据组织方式
列表适用的场景
- 对元素的存储顺序没有要求
- 不需要查找或者排序
- 即数据结构比较简单的时候
下面是用JavaScript实现的一个列表类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
136function List(){
this.listSize = 0;
this.pos = 0;
this.length = length
this.dataStore = [];
this.clear = clear;
this.find = find;
this.toString = toString;
this.insert = insert;
this.append = append;
this.remove = remove;
this.front = front;
this.end = end;
this.prev = prev;
this.next = next;
this.currPos = currPos;
this.moveTo = moveTo;
this.getElement = getElement;
this.contains = contains;
}
// 给列表添加元素
function append(element){
// 先返回原来的listSize作为新元素的索引,在自增1
this.dataStore[this.listSize++] = element;
}
// 删除列表元素
// 要删除首先要找到删除对象
function find(element){
for(let i = 0; i < this.dataStore.length; i++){
if(this.dataStore[i] == element){
return i;
}
return -1;
}
}
function remove(element){
let index = this.find(element);
if(index > -1){
this.dataStore.splice(index, 1);
--this.listSize;
return true;
}
return false;
}
function length(){
return this.listSize;
}
function toString(){
return this.dataStore;
}
function insert(element, after){
let insertPos = this.find(after);
if(insertPos > -1){
this.dataStore.splice(insertPos+1, 0, element);
++this.dataStore;
return true;
}
return false;
}
function clear(){
delete this.dataStore;
this.dataStore = [];
this.listSize = this.pos = 0;
}
function contains(element){
for(let i = 0; i < this.dataStore.length; i++){
if(this.dataStore[i] == element){
return true;
}
}
return false;
}
function front(){
this.pos = 0;
}
function end(){
this.pos = this.listSize - 1;
}
function prev(){
if(this.pos > 0){
--this.pos;
}
}
function next(){
if(this.pos < this.listSize - 1){
++this.pos;
}
}
function currPos(){
return this.pos;
}
function moveTo(position){
this.pos = position;
}
function getElement(){
return this.dataStore[this.pos];
}
// 测试代码
let names = new List();
names.append('a');
names.append('b');
names.append('c');
names.append('d');
names.append('e');
names.front();
console.log(names.getElement());
names.next();
names.next();
names.prev();
console.log(names.getElement());
// 使用迭代器访问列表
// 从前往后遍历
for(names.front(); names.currPos() < names.length(); names.next()){
console.log(names.getElement())
}
// 从后往前遍历
for (names.end(); names.currPos() >= 0; names.prev()) {
console.log(names.getElement())
}
计算机网络——TCP
IP协议只是让两台主机连接起来,数据只是到了主机这个层面,数据真正的通信的主体是主机中的进程
TCP连接是更进一步的连接,是端对端的通信、应用进程之间的通信
首先需要了解TCP报文的一些字段的含义,这对后面的理解非常关键
- SYN:建立连接
- ACK:响应
- FIN:关闭连接
- PSH:有DATA数据传输
- RST:连接重置
这几个字段都可以组合使用,例如:单独SYN为1时,表示请求建立连接;SYN和ACK都为1时,表示发送连接请求之后的响应;关闭连接的FIN的组合同理
TCP建立连接三次握手过程
TCP服务器进程建立TCB传输控制块(被动打开),准备接收客户进程的连接请求,此时服务器进入LISTEN监听状态
第一次:TCP客户进程建立TCB传输控制块(主动打开),向服务器进程发送连接请求报文。报文首部包含一个同步位SYN=1,并选择一个初始序列号seq=x,此时TCP客户端进程进入SYN-SENT同步已发送状态
第二次:TCP服务器进程接收到请求报文后,如果同意连接,就发送确认报文。报文首部包含同步位SYN=1,ACK=1,确认号ack=x+1,自己也要初始化一个序列号seq=y,此时服务器进入SYN-RCVD同步收到状态,之所以自己还要发送一个序列号,这是规定
第三次:客户端进程接收到确认报文后,发送确认报文给服务器进程ACK=1,序列号seq=x+1,确认号ack=y+1
连接确立,客户端进程进入ESTABILISH已建立连接状态,双方可以开始通信
假设就执行的是两次握手的流程,客户端进程发送请求,服务器端进程接收后确认,连接就确立。那么假如客户端进程向服务器端进程发送一条请求,由于各种原因,请求很慢才到达服务器端进程,客户端进程以为服务器端接收不到,又重新发了一次,服务器端接收到后确认建立连接,这次连接就算完成了;但是第一次发送的还在路上的请求到了服务器端那边,服务器端又接收、确立,建立连接,显然第二次连接是重复的,这是不必要的资源浪费
如果是三次握手,那么当服务器端接收到第二次重复请求时,发送确认报文给客户端,但客户端并没有发送确认报文,服务器端就会认为客户端并没有这个请求,也就是识别到这是个无效请求,之间也就不会建立连接
传输过程中的seq和ack代表什么?
- seq是数据包本身的序列号;ack是期望对方继续发送的那个数据包的序列号。
TCP连接四次挥手的过程
首先应该明确客户端进程主动关闭连接,而服务器端进程被动关闭连接
第一次:客户端进程发送连接释放报文 FIN=1,并发送初始序列号seq=u,此时客户端进程进入FIN-WAIT-1状态,客户端进程不再发送数据
第二次:服务器端进程接收到释放报文后,返回一个确认报文ACK=1 ack=u+1 seq=v 此时服务器端进程进入CLOSE-WAIT状态,同时通知高层的应用进程,此时服务器端进程仍可以发送数据,
客户端进程接收到服务器端进程的确认报文后,进入FIN-WAIT-2,等待服务器端进程发送连接释放报文
第三次:等到服务器端进程数据发送完毕后,发送连接释放报文,FIN=1 ACK=1 ack=u+1 seq=w,进入LAST-ACK最后确认状态,等待客户端确认
第四次:客户端进程接收到释放报文后,返回ACK=1 ack=w+1 seq=u+1,进入TIME-WAIT时间等待状态,等待的时间就是2MSL,然后才撤销TCB,进入CLOSED状态,结束TCP连接
服务器端进程接收到确认报文后,就进入CLOSED状态,撤销TCB,结束TCP连接
为什么握手是三次,挥手是四次?
握手时,服务器端进程接收到连接请求后,将确认报文和连接报文都在第二次握手一起发过去;挥手时,服务器端进程接收到释放请求后,先是发送了确认报文,然后再发送释放报文,分两次发送。这就是挥手比握手多一次的原因。
那为什么挥手时要分开发送呢?
要解答这个问题,就要弄懂服务器进程在分开发送的这个过程中,做了些什么?
这段时间属于半关闭状态,服务器端进程通知高层的应用进程、还将一些未发送的数据发送给客户端进程。相当于它还有一些事情没完成,需要给它一点时间,但是又需要给客户端先发送确认,免得客户端以为请求失效了
下面这段引用,顺便讲解了发送ack的目的,主要为了检查对方发送的是否和自己发送的一致
第一次握手:主机A发送位码为syn=1,随机产生seq number=1234567的数据包到服务器,主机B由SYN=1知道,A要求建立联机;
第二次握手:主机B收到请求后要确认联机信息,向A发送ack number=(主机A的seq+1),syn=1,ack=1,随机产生seq=7654321的包;
第三次握手:主机A收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,若正确,主机A会再发送ack number=(主机B的seq+1),ack=1,主机B收到后确认seq值与ack=1则连接建立成功。
完成三次握手,主机A与主机B开始传送数据。
从HTTP状态码301、302到URL劫持
我们都知道HTTP状态码中3xx这系列的都表示重定向,今天这里想说的是301、302这两个状态码
301,表示资源永久性移除到别的地方;302,表示资源暂时性移除到别的地方
共同点是浏览器拿到服务器301或302的状态码之后,都会自动跳转到指定的URL
不同点是遇到301时搜索引擎会抓取新内容,并换上新URL;遇到302时,因为其暂时性,搜索引擎会抓取新内容,保留了原来的URL
当重定向使用的状态码是302暂时性移除时,搜索引擎认为这只是暂时性的,因此就执行的不是那么彻底,所以可能会发生URL劫持,从字面上理解,就是你的网站的URL被偷走了
它的原理是这样的,A网站为自己做了一个重定向,指向B网站,此时当用户输入A网站的URL时,页面将跳转到B网站,用户看到的是B网站的内容;但可能存在一种情况,A网站的URL比较受到搜索引擎的青睐;B网站的URL出于各种原因,不受搜索引擎待见,结果就是用户浏览的是B网站的内容,显示的却是A网站的域名,也就是B网站的URL被A网站劫持了。