文章目录
任务:
- 学习JSON、原生AJAX
- 了解web存储机制
- 用js实现多功能轮播图
- 学习双链表、循环单链表的插入、删除、遍历、修改
JSON基础与使用
JSON 是一种轻量级的数据交换格式,是用于存储和传输数据的格式,通常用于服务端向网页传递数据 。
JSON的优点:
容易阅读和编写。
方便机器解析和生成。
采用独立于程序语言的文本格式,但是也使用了常用编程语言的习惯。这些特性使JSON成为理想的数据交换语言。
JSON的两种数据结构
- 对象结构(无序)
对象结构是使用大括号{}
括起来的,大括号内是由0个或多个用英文逗号分隔的名称 ⁄ 值对构成的。例:
var jsonObj =
{
"键名1":值1,
"键名2":值2,
……
"键名n":值n
}
//键名是字符串,值可以是数值、字符串、对象、数组或逻辑true和false。
- 数组结构(有序)
JSON数组结构是用中括号[]
括起来,中括号内部由0个或多个以英文逗号,
分隔的值列表组成。例:
var arr =
[
{
"键名1":值1,
"键名2":值2
},
{
"键名3":值3,
"键名4":值4
},
……
]
//在JSON数组中,每一对“{}”相当于一个JSON对象
//键名是字符串,值可以是数值、字符串、对象、数组或逻辑true和false。
JSON值的数据类型
- 数值
JavaScript 中的双精度浮点型格式,取决于实现。
不能使用八进制和十六进制格式。
在数字中不能使用NaN
和Infinity
。
数字类型:
- 整形(Integer): 数字1-9,0和正负数
- 分数(Fraction) : 分数,比如 .3,.9
- 指数(Exponent): 指数,比如 e,e+,e-,E,E+,E-
语法:
var json-object-name = { string : number_value, .......}
//例:var obj = {marks: 97}
- 字符串
零个或多个双引号包裹的 Unicode 字符以及反斜杠转义序列。
字符就是只有一个字符的字符串,长度为 1。
如:
语法:
var json-object-name = { string : "string value", .......}
//例 var obj = {name: 'Amit'}
- 布尔值:
true
和false
两个值
语法:
var json-object-name = { string : true/false, .......}
//例 var obj = {name: 'Amit', marks: 97, distinction: true}
- 空格
可以在任意一对符号之间插入。可以添加用来让代码更可读。下面的例子展示了使用空格和不使用空格的声明:
语法:
{string:" ",....}
// var i= " sachin"; 使用空格
// var j = " saurav" 不使用空格
- null
意味着空类型。
语法:
null
// var i = null;
转换为 JS对象
使用内建的 JavaScript eval()
用方法解析JSON数据来生成原生的Javascript对象。eval函数利用的是JavaScript的编译器原理,为了保证语法解析正确,需要将文本包围在括号中。例:
<script>
var txt = '{ "info" : [' +
'{ "name":"张三" , "sex":"男" },' +
'{ "name":"小红" , "sex":"女" },' +
'{ "name":"王五" , "sex":"男" } ]}';
var obj = eval ("(" + txt + ")");
document.getElementById("name").innerHTML=obj.info[0].name
document.getElementById("sex").innerHTML=obj.info[0].sex
</script>
原生AJAX
AJAX是Asynchronous JavaScript and Xml (异步的JavaScript和xml) 的缩写。
异步为它的一个重要特性,即在客户端发起请求之后不需要等待服务器响应,而可以独立的完成其他工作,即在无需重新加载整个页面地基础上实现局部刷新。
XMLHttpRequest对象
实现Ajax需要XMLHttpReques,XMLHttpRequest对象是AJAX中非常重要的对象,所有的AJAX操作都是基于该对象的。
XMLHttpRequest对象用来封装请求报文,我们向服务器发送的请求信息全部都需要封装到该对象中。
XMLHttpRequest在不同版本浏览器获取方式不同,总的来说一共有三种方式:
1 |var xhr = new XMLHttpRequest(); //目前主流浏览器都支持的
2 |var xhr = new ActiveXObject(“Msxml2.XMLHTTP”); //IE6支持的方式
3 |var xhr = new ActiveXObject(“Microsoft.XMLHTTP”); //IE5.5以下支持的方式
- XMLHttpRequest对象的方法
open(method,url,async)
:open()用于设置请求的基本信息,接收三个参数。
method:请求的方法:get或post,接收一个字符串
url:请求的地址,接收一个字符串
Assync:发送的请求是否为异步请求,接收一个布尔值。 true 是异步请求 false 不是异步请求(同步请求)
send(string)
:send()用于将请求发送给服务器,可以接收一个参数
string: 该参数只在发送post请求时需要。
string参数用于设置请求体
setRequestHeader(header,value)
:用于设置请求头
header:字符串类型,要设置的请求头的名字
value:字符串类型,要设置的请求头的值
- XMLHttpRequest对象的属性
readyState
:描述XMLHttpRequest的状态,一共有五种状态分别对应了五个数字:
0 :请求尚未初始化,open()尚未被调用
1 :服务器连接已建立,send()尚未被调用
2 :请求已接收,服务器尚未响应
3 :请求已处理,正在接收服务器发送的响应
4 :请求已处理完毕,且响应已就绪。
status
:请求的响应码
200 响应成功
404 页面未找到
500 服务器内部错误
-
onreadystatechange
:该属性需要指向一个函数
该函数会在readyState
属性发生改变时被调用 -
responseText:获得字符串形式的响应数据。
-
responseXML(用的比较少):获得 XML 形式的响应数据。
GET请求和POST请求
GET和POST都是向服务器发送的一种请求,只是发送的机制不同。
-
使用get请求时,参数在url中显示,而使用post方式,则不会显示出来:
get请求会将参数跟在url后进行传递
post请求作为HTTP消息的实体内容发送给web服务器。 -
使用get请求发送数据量小,只能传递大约1024字节。post请求发送数据量大,可以达到2M
-
get请求需注意缓存问题,post请求不需要:
get方式请求的数据会被浏览器缓存起来,可以从浏览器的历史记录中读取到这些数据,这可能带来安全问题。
双链表
双向链表也叫双链表,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。
双向链表中各节点包含以下 3 部分信息:
指针域(左链域):用于指向当前节点的直接前驱节点;
数据域:用于存储数据元素。
指针域(右链域):用于指向当前节点的直接后继节点;
双向链表结点的定义
//双向链表结点结构体
typedef struct Node
{
int data; //数据
struct Node* prev; //前驱指针
struct Node* next; //后继指针
}Node;
双向链表的创建
同单链表相比,双链表仅是各节点多了一个用于指向直接前驱的指针域。
与单链表不同,双链表创建过程中,每创建一个新节点,都要与其前驱节点建立两次联系,分别是:
将新节点的 prior 指针指向直接前驱节点;
将直接前驱节点的 next 指针指向新节点;
Node* create_node(int data)
{
Node* node = (Node*)malloc(sizeof(Node));//申请内存
node->data = data;
node->prev = NULL;
node->next = NULL;
return node;
}
双向链表初始化
上面代码定义了双向链表的结构体,但是仅仅是声明了双向链表结构体中的元素,并没有对元素进行初始化,所以在使用双向链表之前需要对其进行初始化,让头结点和尾结点等于NULL,链表长度等于0。
void init_list(DoubleList* list)
{
list->head = NULL;
list->tail = NULL;
list->size = 0;
}
获取头结点
Node* get_head(DoubleList* list)
{
return list->head;
}
获取尾结点
Node* get_tail(DoubleList* list)
{
return list->tail;
}
获取链表长度
size_t get_size(DoubleList* list)
{
return list->size;
}
遍历链表
遍历链表打印链表中所有的元素
void show_list(DoubleList* list)
{
Node* node = list->head;
for (int i = 0; i < list->size; ++i)
{
printf("%d ", node->data);
node = node->next;
}
printf("\n");
}
插入头结点
需要考虑两种情况:
- 链表为空,那插入的结点既是头结点,也是尾结点;
- 链表不为空,将新结点和链表通过前驱指针prev和后继指针next连接起来,并将头结点改为新插入的结点;
//插入头结点
void push_head(DoubleList* list, int data)
{
Node* new_node = create_node(data);
//链表为空
if(list->size == 0)
{
//插入的结点既是头结点,也是尾结点
list->head = new_node;
list->tail = new_node;
}
//链表不为空
else
{
//将新结点和链表通过前驱指针prev和后继指针next连接起来
new_node->next = list->head;
list->head->prev = new_node;
//将头结点改为新插入的结点
list->head = new_node;
}
list->size++;
}
插入尾结点
和插入头结点一样,需要考虑两种情况:
- 链表为空,那插入的结点既是尾结点,也是头结点;
- 链表不为空,将新结点和链表通过前驱指针prev和后继指针next连接起来,并将尾结点改为新插入的结点;
//插入尾结点
void push_tail(DoubleList* list, int data)
{
Node* new_node = create_node(data);
//链表为空
if(list->size == 0)
{
//插入的结点既是尾结点,也是头结点
list->tail = new_node;
list->head = new_node;
}
//链表不为空
else
{
//将新结点和链表通过前驱指针prev和后继指针next连接起来
list->tail->next = new_node;
new_node->prev = list->tail;
//将尾结点改为新插入的结点
list->tail = new_node;
}
list->size++;
}
在任意位置插入一个结点
插入前判断插入的位置是否超出链表的范围,排除插入的位置小于0以及大于链表的长度;
插入的位置如果等于0,等同于插入头结点;
插入的位置如果等于链表的长度,等同于插入尾结点;
插入的位置如果小于等于中间位置,那插入的位置更靠近头结点,我们可以从头结点开始往后遍历到插入的前一个位置;
插入的位置如果大于中间位置,那插入的位置更靠近尾结点,我们可以从尾结点开始往前遍历到插入的前一个位置;
bool insert_node(DoubleList* list, int index, int data)
{
//插入的位置超出链表的范围
if(index <0 || index > list->size)
return false;
//插入的位置如果等于0,等同于插入头结点
if(index == 0)
{
push_head(list,data);
return true;
}
//插入的位置如果等于链表的长度,等同于插入尾结点
else if(index == list->size)
{
push_tail(list,data);
return true;
}
Node* tmp = NULL;
//插入的位置如果小于等于中间位置,那插入的位置更靠近头结点
//从头结点开始往后遍历到插入的前一个位置
if(index <= list->size / 2)
{
tmp = list->head;
for(int i=0; i<index-1;i++)
{
tmp = tmp->next;
}
}
//插入的位置如果大于中间位置,那插入的位置更靠近尾结点
//从尾结点开始往前遍历到插入的前一个位置
else
{
tmp = list->tail;
for (int i = 0; i < list->size - index; ++i)
{
tmp = tmp->prev;
}
}
Node* new_node = create_node(data);
tmp->next->prev = new_node;
new_node->next = tmp->next;
tmp->next = new_node;
new_node->prev = tmp;
list->size++;
return true;
}
删除头结点
需要考虑三种情况:
- 链表为空,不操作,直接返回;
- 链表只有一个结点,释放该结点的内存,并初始化链表,将链表长度改为0,头结点和尾结点置为NULL;
- 链表结点大于1,保存头结点,新头结点等于原头结点的下一个结点,然后释放保存的头结点的内存;
//删除头结点
void del_head(DoubleList* list)
{
if(list->size == 0)
return;
if(list->size == 1)
{
free(list->head);//释放内存
init_list(list);
return;
}
Node* tmp = list->head;//保存头结点
list->head = tmp->next;
list->head->prev = NULL;
//释放内存
free(tmp);
tmp = NULL;
list->size--;
}
删除尾结点
和删除头结点类似
- 链表为空,不操作,直接返回;
- 链表只有一个结点,释放该结点的内存,并初始化链表,将链表长度改为0,头结点和尾结点置为NULL;
- 链表结点大于1,保存尾结点,新尾结点等于原尾结点的上一个结点,然后释放保存的尾结点的内存;
//删除尾结点
void del_tail(DoubleList* list)
{
if(list->size == 0)
return;
if(list->size == 1)
{
free(list->tail);//释放内存
init_list(list);
return;
}
Node* tmp = list->tail;//保存尾结点
list->tail = tmp->prev;
list->tail->next = NULL;
free(tmp);//释放内存
tmp = NULL;
list->size--;
}
删除任意位置结点
与在任意位置插入结点类似:
删除前判断插入的位置是否超出链表的范围,排除删除的位置小于0以及大于链表的长度;
删除的位置如果等于0,等同于删除头结点;
删除的位置如果等于链表的长度,等同于删除尾结点;
删除的位置如果小于中间位置,那删除的位置更靠近头结点,我们可以从头结点开始往后遍历到删除的结点;
删除的位置如果大于等于中间位置,那删除的位置更靠近尾结点,我们可以从尾结点开始往前遍历到删除的结点;
//删除任意位置结点
bool del_node(DoubleList* list, int index)
{
//判断插入的位置是否超出链表的范围
if(index < 0 || index > list->size)
return false;
//删除的位置如果等于0,等同于删除头结点
if(index == 0)
{
del_head(list);
return true;
}
//删除的位置如果等于链表的长度,等同于删除尾结点
if(index == list->size)
{
del_tail(list);
return true;
}
Node* tmp = NULL;
//删除的位置如果小于中间位置
if(index < list->size/2)
{
//从头结点开始往后遍历到删除的的结点
tmp = list->head;
for (int i = 0; i < index; ++i)
{
tmp = tmp->next;
}
}
//删除的位置如果大于等于中间位置
else
{
//从尾结点开始往前遍历到删除的结点
tmp = list->tail;
for (int i = 0; i < list->size - index - 1; ++i)
{
tmp = tmp->prev;
}
}
tmp->prev->next = tmp->next;
tmp->next->prev = tmp->prev;
free(tmp);//释放内存
tmp = NULL;
list->size--;
return true;
}
获取任意节点位置
遍历前判断插入的位置是否超出链表的范围,排除删除的位置小于0以及大于链表的长度;
目标结点的位置如果小于中间位置,那目标结点的位置更靠近头结点,我们可以从头结点开始往后遍历到目标结点;
目标结点的位置如果大于等于中间位置,那目标结点的位置更靠近尾结点,我们可以从尾结点开始往前遍历到目标结点;
//获取任意位置结点
Node* get_node(DoubleList* list, int index)
{
//判断插入的位置是否超出链表的范围
if(index < 0 || index > list->size)
return NULL;
Node* node = NULL;
//目标结点的位置如果小于中间位置
if(index < list->size / 2)
{
//从头结点开始往后遍历到目标结点
node = list->head;
for(int i =0; i < index ; i++)
{
node = node->next;
}
}
//目标结点的位置如果大于等于中间位置
else
{
//从尾结点开始往前遍历到目标结点
node = list->tail;
for (int i = 0; i < list->size - index - 1; ++i)
{
node = node->prev;
}
}
return node;
}
获取链表中指定数据所在的第一个结点
Node* find_data(DoubleList* list, int data)
{
if(list->size == 0)
return NULL;
Node* node = list->head;
while(node)
{
if(node->data == data)
return node;
node = node->next;
}
return NULL;
}
修改链表中指定结点的值
先获取到目标位置的结点,再修改目标结点的值
bool modify_node(DoubleList* list, int index, int data)
{
Node* node = get_node(list, index);
if(node)
{
node->data = data;
return true;
}
return false;
}
修改链表中指定的第一个数据
先获取目标数据所在的第一个结点,再修改改结点的值
bool modify_data(DoubleList* list, int data, int val)
{
Node* node = find_data(list, data);
if(node)
{
node->data = val;
return true;
}
return false;
}
释放链表
当链表不需要再使用时,我们需要将其内存释放。
void free_list(DoubleList* list)
{
while(list->size)
{
Node* tmp = list->head;
list->head = list->head->next;
free(tmp);
tmp = NULL;
list->size--;
}
}
循环链表
将单链表中的终端点的指针由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为循环单链表,简称循环链表。
定义结构体
typedef struct SqNode
{
int data;
struct SqNode* Next;
}SqNode,*Sqlist;
链表初始化
Sqlist Initlist(Sqlist L)
{
L = (Sqlist)malloc(sizeof(SqNode));
if (!L)
return NULL;
L->Next = L;
return L;
}
创建循环链表
Sqlist CreateList(Sqlist L)
{
Sqlist s;
Sqlist r;
int Value;
s = L->Next;
printf("请输入您要创建的链表,输入999结束创建\n");
scanf_s("%d", &Value);
getchar();
while (Value!=999)
{
r = (Sqlist)malloc(sizeof(SqNode));
if (!r)
{
printf("内存分配失败,结束创建\n");
return L;
}
r->data = Value;
r->Next = s->Next;
s->Next = r;
s = r;
scanf_s("%d", &Value);
getchar();
}
return L;
}
获取链表长度
int LengthList(Sqlist L)
{
Sqlist s;
s = L->Next;
int Length = 0;
while (s != L)
{
s = s->Next;
Length++;
}
return Length;
}
遍历链表
void PrintfList(Sqlist L)
{
Sqlist s;
s = L->Next;
while (s!=L)
{
printf("%d\t", s->data); //遍历并输出
s = s->Next;
}
printf("\n");
}
插入结点
Sqlist InsertList(Sqlist L, int Locate)
{
Sqlist s;
Sqlist r;
int i=0;
int Value;
int Length = LengthList(L);
s = L;
if (Locate > Length || Locate < 0)
{
printf("您输入的位置超出有效范围\n");
return NULL;
}
while (i<Locate-1 &&s->Next!=L)
{
s=s->Next;
i++;
}
r = (Sqlist)malloc(sizeof(SqNode));
if (!r)
{
printf("申请空间失败\n");
return NULL;
}
s;
printf("请输入您需要插入的数据\n");
scanf_s("%d", &Value);
getchar();
r->data = Value;
r->Next = s->Next;
s->Next = r;
return L;
}
删除结点
Sqlist DeleteList(Sqlist L, int Locate)
{
Sqlist s=L;
Sqlist q;
int Length = 0;
int i = 0;
Length = LengthList(L);
if (Locate > Length || Locate < 0)
{
printf("你输入的位置超出范围\n");
return NULL;
}
while (i<Locate-1 && s->Next!=L)
{
i++;
s = s->Next;
}
q=s->Next;
s->Next = s->Next->Next;
return q;
}