javaScript
1、什么是javaScript
JavaScript是一门世界上最流行的脚本语言
Java、JavaScript
ECMAScript:javaScript的一个标准
2、快速入门
2.1、引入javaScript
内部引入:
<script>这里写javaScript代码</script>
外部引入:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>第一个javaScript程序</title>
<!-- script标签内书写javaScript代码-->
<script>
alert("helloWorld");
</script>
<script src="js/lbt.js"></script>
<!-- 不书写type,则默认为text/javascript-->
<script type="text/javascript"></script>
</head>
<body>
<!--这里也可以书写script标签-->
</body>
</html>
2.2、基本语法入门
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>基本语法</title>
<!--javaScript严格区分大小写-->
<script>
//1、定义变量 变量类型 变量名 = 值
var lbt="liubaoteng";
var age=22;
//alert(lbt+":"+age);
//2、条件控制
if(true){
alert(true);
}
//3、在浏览器控制台打印变量
console.log(lbt+":"+age)
</script>
</head>
<body>
</body>
</html>
2.3、数据类型
数值、文本、图形、音频、视频。。。。。
1、number:js不区分小数和整数
(NaN:not a number Infinity:表示无限大)
2、字符串
3、布尔值
4、逻辑运算
5、比较运算符
=:赋值
==:等于(类型不一样,值一样,也会判断为true)
===:绝对等于(类型一样,值一样,结果为true)
这是js的缺陷,坚持不要使用==比较
注意:
NaN===NaN,这个与所有的数值都不相等,包括自己
只能通过isNaN(NaN)来判断这个数是否为Nan
浮点数问题:
eg: 1/3===1-2/3 结果为false
尽量避免使用浮点数进行运算,存在精度问题。
Math.abs(1/3-(1-2/3))<0.0000000001(可以使用这种方法)
6、null和underfined
null 空
underfined 未定义
7、数组
java的数组必须为一系列相同类型的对象,js不需要这样
var arr=[1,2,3,4,5,“lbt”,‘liubait’];
new Array(1,2,3,4,5,“lbt”,‘liubait’);
(为保证代码可读性,尽量使用第一种[ ])
取数组小标,如果越界,则会显示underdined
8、对象
对象是大括号{ },数组是中括号[ ]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试</title>
<script type="text/javascript">
var person = {
name:"lbt",
age:23,
tags:['javaScript','java','javaWeb']
}
</script>
</head>
<body>
</body>
</html>
每个属性之间使用逗号隔开,最后一个不需要添加。
2.4严格检查格式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>严格检查模式</title>
<!--
前提:IDEA设置支持ES6语法
use strict代表严格检查模式,预防javaScript的随意性导致的一些问题,必须写在javaScript第一行
局部变量建议都是用let定义
-->
<script>
'use strict';
//i=1;//默认全局变量
i=1;
// let i =1;//es6中,局部变量用let定义。
</script>
</head>
<body>
</body>
</html>
3、数据类型
3.1、字符串
1、正常字符串使用单引号‘’,或双引号“”包裹
2、注意转义字符
3、多行字符串编写
··: tab键上面
4、模板字符串
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>字符串</title>
<script>
'use strict';
let name="刘宝腾";
let age = 23;
var message=`
你好${name},
你今年${age}岁了。
`;
console.log(message);
</script>
</head>
<body>
$END$
</body>
</html>
5、字符号长度
str.length
6、字符串的不可变性
7、大小写转换
注意这里是方法,不是属性
str.toUpperCase();
str.toLowerCase();
8、获取指定字符下标
str.indexOf(' ');
9、字符串截取
str.subString(1);//从第一个字符串截取到最后一个字符串
str.subString(1,3);//包含第一个不包含第三个。
3.2、数组
Array可以包含任意类型的数据类型
var arr=[1,2,3,4,“lbt”,a];
arr[0];//取值
arr[0]=1;//赋值
1、长度
arr.length
注意:如果给arr.length赋值,数组大小就会发生变化。如果赋值过小,元素就会丢失。
2、indexOf,通过元素获得下标索引
3、slice()//截取Array的一部分,返回一个新数组。
4、push()、pop()
push:压入指定元素到尾部
pop:弹出尾部的一个元素
5、unshift()、shift()
unshift:压入指定元素到头部
shift:弹出头部的一个元素
6、排序sort()
arr.sort()
7、元素反转reverse()
arr.reverse()
8、concat()
arr.concat([1,2,3])
注意:concat()并没有修改数组,只是会返回一个新的数组。
9、连接符join
打印拼接数组,使用特定的字符串连接
arr.join(’-’);
10、多维数组
arr=[[1,2],[3,4],[5,6]];
arr[1][1];
数组:存储数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>数组</title>
<script>
let arr=[1,2,3,34,5,67]
console.log("取值:"+arr[1]);
arr[1] = 55;//赋值
console.log("取值:"+arr[1]);
console.log("数组长度:"+arr.length)
console.log("通过元素获得下标索引:"+arr.indexOf(3));
console.log("截取Array的一部分,返回一个新数组:"+arr.slice(1,5))
arr.push('lbt');
console.log("压入指定元素到尾部:"+arr);
arr.pop();
console.log("弹出尾部的一个元素:"+arr);
arr.unshift(23);
console.log("压入指定元素到头部:"+arr);
arr.shift();
console.log("弹出头部的一个元素:"+arr);
console.log("排序:"+arr.sort());
console.log("反转:"+arr.reverse());
console.log("concat()并没有修改数组,只是会返回一个新的数组"+arr.concat([9,8,7])+"原数据:"+arr);
console.log("连接符join:"+arr.join('--'))
//多维数组
let arrd=[1,[2,3],[45,5,6]];
console.log("多维数组取值:"+arrd[1][1]);
</script>
</head>
<body>
</body>
</html>
3.3、对象
若干个键值对
var 对象名={
属性名:属性值,
属性名:属性值,
属性名:属性值
}
let person = {
name:“lbt”,
age:23,
phone:“15620178661”
}
js中的对象{…},多个属性之间使用逗号隔开,最后一个属性不加逗号。
JavaScript中所有的键都是字符串,值是任意对象
1、对象赋值
person.name=‘liuboateng’;
2、使用一个不存在的对象属性,不会报错。
person.abcd
underfined
3、动态的删减属性:delete
delete person.name
true
4、动态添加属性,直接给新的属性添加值即可
person.abcd=‘abcd’
5、判断属性值是否在这个对象中
‘属性名’ in person
eg:
‘age’ in person
true
//继承
‘toString’ in person
true
6、判断一个属性是否是这个对象自身拥有的:
hasOwnProperty().
person.hasOwnProperty(‘toString’);
false
person.hasOwnProperty(‘age’)
true
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>对象</title>
<script>
let person = {
name:"lbt",
age:23,
phone:"15620178661"
}
//赋值
person.name='liubaoteng';
console.log(person)
//使用一个不存在的对象属性,不会报错
console.log(person.abxd);
//动态的删减属性:delete
delete person.age
console.log(person);
//动态添加属性,直接给新的属性添加值即可
person.abc='test';
console.log(person);
//判断属性值是否在这个对象中
console.log('toString' in person)//true 因为继承
console.log('name' in person)//true
//判断一个属性是否是这个对象自身拥有的
console.log(person.hasOwnProperty('toString'));//false
console.log(person.hasOwnProperty('name'));//true
</script>
</head>
<body>
</body>
</html>
3.4、流程控制
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>流程控制</title>
<script>
let age=3;
//if判断
// if(age<3){
// alert("小于三岁");
// }else if(age>=3&&age<=10){
// alert("大于等于三岁小于等于十岁");
// }else{
// alert("大于十岁")
// }
//循环
// while (age<50){
// age++;
// console.log(age);
// }
// for (let i = 0; i < 100; i++) {
// age++;
// console.log(age);
// }
let split =[45,"ewrew",43,21];
// split.forEach(function (value, index, array) {
// console.log(value);
// })
// for (const splitKey in split) {
// console.log(split[splitKey])
// }
for (const splitElement of split) {
console.log(splitElement)
}
</script>
</head>
<body>
</body>
</html>
3.5、Map和Set、iterator
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Map和Set</title>
<script>
let map = new Map([['lbt',95],['ly',98],['zzf',88]]);
let ly = map.get("ly");
console.log(ly);
map.set('lpp',91);
console.log(map)
console.log('---------------------')
let set = new Set([1,2,3,3,21,2,3,2,2,1]);
console.log(set)
set.delete(1);//删除元素
console.log(set)
set.add(24);//添加元素
console.log(set)
let b = set.has(2);//是否包含某个元素
console.log(b)
console.log('===============');
//遍历map
// for (const x of map) {
// console.log(x);
// }
//遍历set
for (const x of set) {
console.log(x);
}
</script>
</head>
<body>
</body>
</html>
4、函数
4.1、定义函数
//绝对值
//定义方式一
function jdz(x) {
//手动定义异常
// if(typeof x !=='number'){
// throw 'x is not a number!';
// }
//arguments关键字
console.log(arguments);
if(x>=0){
return x;
}else{
return -x;
}
}
//定义方式二
let jdz2 = function jdz(x) {
if(x>=0){
return x;
}else{
return -x;
}
}
一旦执行return,代表函数结束,返回结果。
如果没有执行return,函数执行完也会返回结果,结果为underfined。
**arguments关键字(获取所传递的所有参数):代表传递进来的所有参数是一个数组。
**…rest关键字:ES6一如的新特性,获取除了已经定义的参数以外的参数。(…为关键字标志,后面rest名可更改)
//…rest关键字
//...rest关键字
function testRest(a,b,...rest) {
for(let i=0;i<arguments.length;i++){
console.log("参数明细:"+arguments[i]);
}
console.log("全部参数:"+arguments);
console.log("未定义参数:"+rest);
}
4.2、变量的作用域
在JavaScript中,var定义变量实际是有作用域的,假设是在函数体中声明,则在函数体外不可引用(非要想实现可以研究闭包)。
'use strict'
function zyy() {
var x =1;
x=x+1;
}
// x=x+1; Uncaught ReferenceError: x is not defined
如果两个函数使用了相同的变量名,只要在函数内部,就不冲突。
'use strict'
function zyy() {
var x =1;
x=x+1;
}
// x=x+1; Uncaught ReferenceError: x is not defined
function zyy2() {
var x =1;
x=x+1;
function zyy21() {
var y = x+1;
}
// var z = x+y; Uncaught ReferenceError: y is not defined
}
内部函数可以访问外部函数的成员,反之则不行。
function zyy2() {
var x =1;
x=x+1;
function zyy21() {
var y = x+1;
}
// var z = x+y; Uncaught ReferenceError: y is not defined
}
如果内部函数变量和外部函数变量重名,则使用就近原则,从自身函数开始,由内向外查找。
提升变量的作用域:
function zyy3() {
var x = 'x'+y;
console.log(x);
var y='y';
}
//结果:xundefined
说明:js执行引擎,自动提升了y的声明,但不会提升y的赋值。
养成规范:所有变量定义在函数的头部,不要乱放,便于代码维护。
function zyy4() {
var a=1,
b,c,d;
b=a;
//之后随便用
}
全局变量
var qj=0;
function zyy5() {
qj = qj+1;
console.log(qj)
}
zyy5();
qj = qj+1;
console.log;
var x='xxx';
alert(x);
alert(window.x);//默认所有的全局变量都绑定在window下
//结果:1,2
全局对象window
var x=‘xxx’;
alert(x);
alert(window.x)//默认所有的全局变量都绑定在window下
alert()这个函数本身也是一个window变量
规范:由于我们所有的全部变量都会绑定到我们的window上,如果不同的js文件使用了相同的全局变量,则造成冲突,解决办法:
var liubaotengApp = {};
liubaotengApp.name='lbt';
liubaotengApp.age=23;
liubaotengApp.method1 = function (a,b) {
return a+b;
}
把自己的代码全部放入自己定义的唯一空间名字中,降低全局命名冲突的问题。
局部作用域let:
es6关键字,解决局部作用域冲突的问题,建议都使用let定义局部作用于的变量。
//局部作用域let
//局部作用域let
function zyy6() {
for (let i = 0; i <=100 ; i++) {
console.log(i);
}
console.log(i+1);//Uncaught ReferenceError: i is not defined
}
zyy6();
常量const:
在es6之前,定义常量(约定):只有用全部大写字母命名的变量就是常量,实际可以修改。
在es6引入了常量关键字const
//常量const
//常量const
const lbt='刘宝腾';
console.log(lbt);
lbt = '旅游';//Uncaught TypeError: invalid assignment to const 'lbt'
4.3、方法
定义方法(方法就是把函数放在对象里面,对象只有两个东西:属性和方法)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>方法</title>
<script>
// var lbt={
// name:'刘宝腾',
// birthday:1998,
// age:function () {
// return new Date().getFullYear()-this.birthday
// }
// };
function getAge() {
return new Date().getFullYear()-this.birthday;
}
var lbt={
name:'刘宝腾',
birthday:1998,
age:getAge
};
//lbt.age() -->23
//getAge() -->NaN
getAge.apply(lbt,[]); // lbt:this指向的对象,[]:方法所传参数为空 输出结果:23
</script>
</head>
<body>
</body>
</html>
在Java中:this是无法指向的,是默认指向调用它的对象
但在js中可以控制this指向。
getAge.apply(lbt,[]); // lbt:this指向的对象,[]:方法所传参数为空 输出结果:23
5、内部对象
标准对象
typeof 123
“number”
typeof ‘123’
“string”
typeof true
“boolean”
typeof []
“object”
typeof Math.abs
“function”
typeof NaN
“number”
typeof undefined
“undefined”
5.1、Date
基本使用:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>date</title>
<script>
var now = new Date();//Date Sun Apr 11 2021 00:18:52 GMT+0800 (中国标准时间)
now.getFullYear();//年
now.getMonth();//月
now.getDate();//日
now.getDay();//星期几
now.getHours();//时
now.getMinutes();//分
now.getSeconds();//秒
now.getTime();//时间戳
new Date(1618071532391);//时间戳转时间
now.toLocaleString();//本地时间 注意:是一个方法,不是属性
</script>
</head>
<body>
</body>
</html>
转换:
now.toLocaleString();//本地时间 注意:是一个方法,不是属性
5.2、JSON
在JavaScript一切皆为对象,任何js支持的类型都可以用JSON来表示。
格式:
对象都用{}
数组都用[]
所有的键值对都用key:value
JSON字符串和JS对象的转化:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSON</title>
<script>
var user = {
'name':'刘宝腾',
'age':23,
'sex':'男'
}
console.log(user);
//对象转换为JSON字符串
let s = JSON.stringify(user);
console.log(s);
//JSON字符串转换为对象
let parse = JSON.parse(s);
console.log(parse);
</script>
</head>
<body>
</body>
</html>
5.3、Ajax
1、原生的js写法 xhr异步请求
2、jquery封装好的方法$("#name").ajax("")
3、axios请求
6、面向对象编程
JavaScript、Java、c#…面向对象;JavaScript有一些区别。
类:模板
对象:具体的实例
在JavaScript中需要换一下思维方式:
原型:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>什么是面向对象</title>
<script>
var student={
name:'刘宝腾',
age:23,
sex:'男',
hobby:function () {
console.log(this.name+'run....')
}
};
var ly={
name:'刘月'
};
ly.__proto__=student;
</script>
</head>
<body>
</body>
</html>
//ly的原型是student
ly.proto=student;
class继承
class关键字是在ES6引入的。
1、定义一个类(属性和方法)
//定义一个学生类
class Student{
constructor(name) {
this.name=name;
}
hobby(){
console.log("play games");
}
}
var lbt = new Student("lbt");
2、继承
//继承
class Primary extends Student{
constructor(name,grade) {
super(name);
this.grade=grade;
}
primaryHobby(){
console.log("study");
}
}
var ywk = new Primary('杨文坤','五年级');
本质:查看对象原型。
原型链 proto:
7、操作BOM对象(重点)
JavaScript和浏览器的关系:
JavaScript诞生就是为了能够在浏览器中运行。
BOM:浏览器对象模型
浏览器:IE6~11
Chrome
Safair
FireFox
window(重要)
window代表浏览器窗口:
window.alert();
window.innerHeight
window.innerwidth
window.outerHeight
window.outerwidth
…
Navigator(不建议使用)了浏览器的信息:
navigator.appVersion
navigator.userAgent
navigator.platform
大多数时候,我们不会使用navigator对象,因为会被人为修改。
不建议使用这些属性来判断和编写代码
screen:代表屏幕尺寸
screen.width
screen.height
location
代表当前页面的URL信息
host: “www.bilibili.com”
href: “https://www.bilibili.com/video/BV1JJ41177di?p=19&spm_id_from=pageDriver”
protocol: “https:”
reload: function reload()//刷新网页
location.assign(‘http://www.baodu.com’)//设置新的地址
document(内容)
代表当前的页面,HTML DOM文档树
document.title
“百度一下,你就知道”
document.title=‘刘宝腾’
“刘宝腾”
获取具体的文档树节点:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>document</title>
</head>
<body>
<dl id="dl">
<dd>java</dd>
<dt>javaSE</dt>
<dt>javaEE</dt>
</dl>
<script>
var dl = document.getElementById('dl');
console.log(dl);
</script>
</body>
</html>
获取cookie
document.cookie
服务器端可以设置cookie属性为httponly,只读
history(不建议使用)
代表浏览器的历史记录
history.back();//后退
history.forward();//前进
操作DOM对象(重点)
DOM:文档对象模型
浏览器网页就是一个DOM树形结构
更新:更新DOM节点
遍历DOM节点:得到DOM节点
删除:删除一个DOM节点
添加:添加一个新的节点
要操作一个DOM节点,就必须要获得这个节点。
获取DOM节点:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>获取DOM节点、删除DOM节点</title>
</head>
<body>
<div id="father">
<h1>标签</h1>
<p id="idchoose">id选择器</p>
<p class="classChoose">类选择器</p>
</div>
<script>
//对应css的选择器
let elementsByTagName = document.getElementsByTagName("h1");
let elementById = document.getElementById("idchoose");
let elementsByClassName = document.getElementsByClassName("classChoose");
console.log(elementsByTagName);
console.log(elementById);
console.log(elementsByClassName);
father.children;//获取父节点下的所有子节点
//删除idchoonse
let elementById = document.getElementById("idchoose");
let father = document.getElementById("father");
father.removeChild(elementById);
</script>
</body>
</html>
更新DOM节点
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>更新DOM节点</title>
</head>
<body>
<div id="id1"></div>
<script>
let id1 = document.getElementById('id1');
id1.innerText='lbt'
id1.innerHTML='<h1>lbt</h1>';
id1.style.color='red';
id1.style.fontSize='200px';
</script>
</body>
</html>
删除DOM节点
步骤:先获取父节点、在通过父节点删除自己
<div id="father">
<h1>标签</h1>
<p id="idchoose">id选择器</p>
<p class="classChoose">类选择器</p>
</div>
//删除idchoose
let elementById = document.getElementById("idchoose");
let father = document.getElementById("father");
father.removeChild(elementById);
注意:删除多个节点的时候,children是在时刻变化的。
创建和插入节点
我们获得了某个DOM节点,假设这个DOM节点是空的,我们通过innerHTML就可以增加一个元素,但是这个DOM节点已经存在元素了,我们就不能这么做,因为会产生覆盖。
追加元素:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>插入节点</title>
</head>
<body>
<p id="javaScript" >javaScript</p>
<div id="list">
<p id="javaSE">javaSE</p>
<p id="javaEE">javaEE</p>
<p id="javaME">javaME</p>
</div>
<script>
//讲JavaScript转移到div标签内
let javaScript = document.getElementById('javaScript');//已存在的节点
let list = document.getElementById('list');
list.appendChild(javaScript);
//通过JavaScript创建一个新的节点
let newp = document.createElement('p');
newp.id='newp';
newp.innerText='javaWeb';
list.appendChild(newp);
//创建一个新的标签(通过这个属性可以设置任意的值)
let myScript = document.createElement('script');
myScript.setAttribute('type','type/javaScript');
list.appendChild(myScript);
//修改背景颜色
let elementsByTagName = document.getElementsByTagName('body');
elementsByTagName[0].style.backgroundColor='red';
elementsByTagName[0].setAttribute('style','background-color: blue')
//将javaME放到javaEE之前
let javaEE = document.getElementById('javaEE');
let javaME = document.getElementById('javaME');
list.insertBefore(javaME,javaEE);
</script>
</body>
</html>
9、操作表单(验证)
表单是什么 form DOM树
文本框 text、
下拉框、
单选框radio、
多选框checkbox、
隐藏域hidden、
密码框password
。。。。。。
表单的目的:提交信息。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>操作表单</title>
<script src = " https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.min.js "></script>
</head>
<body>
<!--onsubmit 绑定提交事件 true false
将结果返回给表单,使用onsubmit接受收。
-->
<form action="https://www.baidu.com" method="post" onsubmit="return aaa()">
<p>用户名:</p><input type="text" id="username" name="username">
<p>密码:</p><input type="password" id="password" >
<input type="hidden" id="md5-password" name="password">
<!--绑定事件onclick-->
<button type="submit">提交</button>
</form>
<script>
function aaa() {
let username = document.getElementById("username");
let password = document.getElementById("password");
let md5password = document.getElementById("md5-password");
console.log(username.value);
console.log(password.value);
md5password.value=md5(password.value);
console.log(md5password.value)
return true;
}
</script>
</body>
</html>
10、jquery
中文文档:https://jquery.cuishifeng.cn/
JavaScript和jQuery库
jQuery库,里面存在大量的javaScript函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- cdn引入-->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<!-- 本地jQuery-->
<!-- <script src="jsDev/jquery-3.6.0.js"></script>-->
<!--公式: $(selector).action() -->
</head>
<body>
<h4 id="testJquery">点我</h4>
<script>
<!-- 选择器就是css的选择器-->
$("#testJquery").click(function () {
alert("生效了")
})
</script>
</body>
</html>
jQuery选择器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>选择器</title>
</head>
<body>
<script>
//原生js
//标签
document.getElementsByTagName();
//类
document.getElementsByClassName();
//id
document.getElementById();
//jQuery
//标签
$("p").click();
//类
$(".class").click();
//id
$("#id").click();
</script>
</body>
</html>
jQuery事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Jquery事件</title>
<script src="jsDev/jquery-3.6.0.js"></script>
</head>
<body id="divMove" style="height: 2000px">
获取鼠标坐标:<span id="mouse"></span>
<script>
$(function () {
$("#divMove").mousemove(function (e) {
$("#mouse").text("x:"+e.pageX+";y:"+e.pageY);
})
})
</script>
</body>
</html>
技巧:
1、巩固js:看框架源码(jquery)、看游戏源码
2、巩固html和css(扒网站,全部down下来,对应看效果)
推荐配套源码学习:https://download.csdn.net/download/rookie_lbt/19103293