文章目录
一、JavaScript基础
1.变量(重点)
变量指的是在程序中保存数据的一个容器
语法:var 变量名 = 值
1-1 定义变量及赋值
//定义一个变量
var num
//给一个变量赋值
num = 100
//定义一个变量的同时给其赋值
var num2 = 200
- 注意:
- 一个变量名只能存储一个值
- 当再次给一个变量赋值的时候,前面一次的值就没有了
- 变量名称区分大小写(
js
严格区分大小写)
1-2 变量的命名规则和命名规范
规则:必须需遵守的
- 一个变量名称可以由**数字、字母、英文下划线(__)、美元符号($)**组成
- 严格区分大小写
- 不能由数字开头
- 不能是保留字或者关键字
- 不要出现空格
规范:建议遵守
- 变量名尽量有意义(语义化)
- 遵循骆驼命名规则,有多个单词组成的时候,从第二个单词开始首字母大写
- 不要使用中文
判断数据类型:
使用
typeof
关键字来进行判断//第一种使用方式 var n1 = 100 console.log(typeof n1) //第二种使用方式 var s1 = 'abc' console.log(typeof(s1))
2.数据类型转换
- 数据类型之间的转换,比如数字转成字符串,字符串转成布尔,布尔转成数字等
2-1 其他数据类型转成数值
Number(变量)
可以把一个变量强制转换成数值类型
可以转换小数,会保留小数
可以转换成布尔值
以下都是:遇到不可转换的都会返回
NaN
parseInt(变量)
从第一位开始检查,是数字就转换,直到一个不是数字的内容
开头不是数字就直接返回
NaN
不认识小数点,只能保留整数
parseFloat(变量)
从第一位开始检查,是数字就转换,直到一个不是数字的内容
开头不是数字就直接返回
NaN
认识一次小数点
2-2 其他数据类型转成字符串
变量.toString()
有一些数据类型不能用
toString()
方法,如:undefined
和null
String(变量)
所有数据类型都能用
使用加法运算
字符串拼接:有一方是字符串就会拼接
加法运算:只有两边都是数字才会进行数学运算
2-3 其他数据类型转成布尔
Boolean(变量)
在js中,只有
''
、0
、null
、undefined
、NaN
,这些是false
,其他都是true
3.函数
对于
js
来说,函数就是把任意一段代码放在一个盒子里面代码:
//这个是我们以前写的一段代码 for (var i = 0 ; i < 10 ; i++) { console.log(i) } //函数,这个 {} 就是那个“盒子” function fu() { //这个函数我们以前写的代码 for (var i = 0 ; i < 10 ; i++) { console.log(i) } }
3-1函数定义阶段
声明式
使用
function
这个关键字来声明一个函数语法:
function fn() { //一段代码 } //function:声明函数的关键字 //fn:函数的名字,自己定义 //():必须写,放参数的位置 //{} : 就是放一段代码的位置也就是刚刚说的“盒子”
赋值式
其实就是使用
var
关键字一样首先使用
var
定义一个变量,把一个函数当做值直接复制给这个变量就可以了语法:
var fn = function() { //一段代码 }
3-2函数调用阶段
调用一个函数
- 函数调用就是直接谢
函数名()
即可
//声明式函数 function fn() { console.log("我是fn函数") } //调用函数 fn() //赋值式函数 var f1 = function(){ console.log("我是f1函数") } //调用函数 f1()
- 注意:定义完一个函数以后,如果没有调用函数,那么写在{}里面的代码没有意义,只有调用以后才会执行
3-3 调用上的区别
- 声明式函数:调用可以在定义之前或者定义之后
- 赋值式函数:调用只能在定义之后
3-4 函数的参数
参数分为两种形参和实参
//声明式 function fn(形参) { 一段代码 } fn(实参) //赋值式一样的位置,写在"()"里面
3-5 函数的定义域
访问规则
- 首先,在自己的作用域内部查找,如果有,就直接拿来用
- 如果没有,就去上一级作用于查找,如果有,就拿来用
- 以此类推
- 如果一直没有这个变量就会直接报错
var num = 100 function fn() { var num2 = 200 function fn2() { var num3 = 300 console.log(num3)//300 console.log(num2)//200 console.log(num)//100 console.log(a)//都没有就是直接报错 } fn2() } fn()
- 变量的访问规则也叫做作用域的查询机制
- 作用域的查找机制只能是向上查找不能向下找
function fn() { var num = 100 } fn() console.log(num)//发现自己作用域没有,自己就是全局作用域,没有上一级了,直接报错
赋值规则
- 先在自己作用域内部查找,有就直接赋值
- 没有就去上一级作用域内部查找,有就直接赋值
- 以此类推
- 如果一直找到全局作用域都没有,那么就把这个变量定义为全局变量,再给他赋值
4.对象
对象是一个复杂数据类型
存储了一些基本数据类型的一个集合
var obj = { num : 100, str : 'hello world', boo : true }
这里的
{}
和函数中的{}
不一样函数里面的是写代码的,而对象里面是写一些数据的
对象就是一个键值对的集合
{}
里面的每一个键都是一个成员
4-1 创建对象
字面量的方式创建一个对象
//创建一个空对象 var obj = {} //向对象中添加成员 obj.name = 'jack' obj.age = 18
内置构造函数的方式创建对象
//创建一个空对象 var obj = new Object() //向对象中添加成员 obj.name = 'jack' obj.age = 18
Object
是js
内置给我们的构造函数,用于创建一个对象使用的
4-2 数据类型之间存储的区别
- 我们的存储空间分成两种栈和堆
- 栈:主要存储基本数据类型的内容
- 堆:主要存储复杂数据类型的内容
基本数据类型在内存中的存储情况
var num = 100
,在内存中的存储情况- 直接在
栈空间
内有存储一个数据
复杂数据类型在内存中的存储情况
下面这个对象的存储
var obj = { name : "java", age : 18, gender : '男' }
复杂数据类型的存储
- 在堆里面开辟一个存储空间
- 把数据存储到存储空间内
- 把存储空间的地址赋值给栈里面的变量
5.数组
5-1 数组的常用方法
push() :在数组的末尾添加一个或多个元素,并返回新的长度
pop() :删除数组的最后一个元素,并返回该元素的值
unshift() :在数组的开头添加一个或多个元素,并返回新的长度
shift() :删除数组的第一个元素,并返回该元素的值
splice() :从数组中添加或删除元素
//splice() -> 删除元素: var arr = [1 , 2 , 3 , 4] var ressplice = arr.splice(1 , 2) //第一个参数是要开始删除的位置下标 //第二个参数是要删除元素的个数 //ressplice表示返回值,是删除元素组成的集合 //splice() -> 增加元素: var arr1 = [1 , 2 , 3 , 4] var ressplice1 = arr.splice(1 , 0 , "zhangsan" , "lisi") //后面两个参数是增加的元素 //前面两个表示在第一个元素的位置不删除元素,但是在这个位置增加两个元素
sort() :对数组的元素进行排序
//不能直接用arr.sort()的用法 arr.sort(function(a , b){ return a - b //表示从小到大排序 //return b - a 表示从大到小排序 })
reverse() :颠倒数组中元素的顺序
以上方法都会对原数组造成影响
concat() :连接两个或多个数组,并返回新的数组,并不会改变原来的数组
slice() :从数组中截取一部分,并返回新的数组,并不会影响原数组
var arr = [1 , 2 , 3 , 4] var arr2 = arr.slice(0 , 2) //[1 , 2] ,截取的数组是左闭右开 //-1表示最后一个数,可以用负数表示
indexOf() :返回数组中指定元素的第一个索引,-1就表示找不到
lastIndexOf() :返回数组中指定元素的最后一个索引,-1就表示找不到
join() :将数组的所有元素连接成一个字符串
arr.join()//默认会用“,”隔开 arr.join("|")//会用“|”隔开,里面可以是任意字符
forEach() :对数组的每个元素执行一次指定的函数
//forEach()遍历 var arr = ["aaa" , "bbb" , "ccc"] //回调函数 arr.forEach(function(item , index , arr) { console.log(item , index , arr) }) //item : 表示数组里面的值 //index : 表示当前位置的索引 //arr : 表示数组,每次都会重新将数组打印一遍
map() :对数组的每个元素执行一次指定的函数,并返回一个新的数组
var arr = [1 , 2 , 3 , 4] //回调函数 var arr1 = arr.map(function(item) { return item * item }) console.log(arr1) //表示将arr数组中每个数的平方加入到arr1数组中
filter() :对数组的每个元素执行一次指定的函数,并返回一个新的数组,该数组只包含满足条件的元素(只会返回
true
和false
)var arr = [1 , 2 , 3 , 4] //回调函数 var arr1 = arr.filter(function(item) { return item > 2 }) console.log(arr1) //只包含[3 , 4] ,把1 和 2都过滤掉了
every() :对数组的每个元素执行一次指定的函数,如果所有元素都满足条件,则返回 true
var arr = [1 , 2 , 3 , 4] //回调函数 var arr1 = arr.every(function(item) { return item > 2 }) console.log(arr1) //返回false,因为前面两个数不满足 //[3 , 4 , 5]返回true,因为所有的元素都满足条件
some() :对数组的每个元素执行一次指定的函数,如果有任意一个元素满足条件,则返回 true
reduce() :对数组的每个元素执行一次指定的函数,并返回一个值
var arr = [1 , 2 , 3 , 4 , 5] var arr1 = arr.reduce(function(prev , item){ return prev + item } , 0) console.log(arr1)//输出15,表示所有数字累加 //prev表示每次计算后的结果,开始数据为后面的写入数据为0,(也可以写"",这样就表示字符串的拼接操作) //item表示当前数组的值
reduceRight() :对数组的每个元素执行一次指定的函数,并返回一个值,该值是从右到左计算的
find() :返回数组中第一个满足条件的元素
findIndex() :返回数组中第一个满足条件的元素的索引
fill() :用指定的值填充数组的指定位置
copyWithin() :将数组的一部分复制到另一个位置
6.字符串
6-1 字符串的常用方法
charAt():返回指定位置的字符
charCodeAt():返回指定位置的字符的 Unicode 编码
fromCharCode():将 Unicode 编码转换为字符
toUpperCase():将字符串转换为大写
toLowerCase():将字符串转换为小写
substr(开始索引,长度):返回指定位置的子字符串
substring(开始索引,结束索引):返回指定位置的子字符串(左闭右开,不能用负数)
slice(开始索引,结束索引):返回指定位置的子字符串(左闭右开,可以使用负数)
replace():替换字符串中的指定字符
var str = "asdfsdafdsaf" var str1 = str.replace("a" , "*") //第一个参数"a"表示要修改的字符 //第二个参数"*"表示将原来的字符修改成这个 //注意不能全部修改只能修改第一次出现的,并不是把所有的"a"改成"*",只能改第一个
split():将字符串分割为数组
var str = "a|b|c|d" console.log(str.split("|")) //表示将原来的字符串按照"|"进行分割形成一个新数组["a" , "b" , "c" , "d"]
indexOf():返回指定字符在字符串中第一次出现的位置
var str = "adsafdsaf" console.log(str.indexOf("a")) //返回第一次出现"a"的下标 console.log(str.indexOf("a" , 1)) //表示从索引1开始往后查的第一个"a"的下标,在1前面的不用算
lastIndexOf():返回指定字符在字符串中最后一次出现的位置
concat():连接字符串
trim():去除字符串两端的空格
trimStart() 和 trimLeft() :去掉首空格
trimEnd() 和 trimRight() : 去掉尾空格
6-2 json字符串(主要用来前后端交流)
json字符串的格式:'{"key" : 110 , "key" : "110"}'
- 里面用了" ",最外面就要用’ ’
- key值必须要用" "包裹
- value值随便,如果是字符串就用"",如果是数字就直接写
- 不能有多余的”,“ 只能是数据之间用逗号隔开
json字符串转为对象
var str = '{"name" : "nb" , "age" : 18}' var obj = JSON.parse(str) console.log(obj)
对象转为json字符串
var obj = {name "tiechui"} var str = JSON.stringify(obj) console.log(obj , str)
6-3 模板字符串
普通字符串用单引号或双引号来表示,而模板字符串用``来表示
var name = "aaa\
bbb\
ccc
"
//普通字符串想要换行输入就要加上"\"
var name1 = `aaa
bbb
ccc`
//模板字符串就可以直接换行
var myname = "xie"
var str = `my name is ${myname} ${10 + 20} ${10 > 20 ? 'aaa' : 'bbb'}`
//这种写法会把${}里面的数据自动解析,如myname会自动写成xie
7.数字
7-1 数字的常用方法
toFixed():保留几位小数,返回的是字符串
var price = 123.4567 var sum = price.toFixed(2) - 0 + 100 //因为返回的是字符串,所以要先减去0,变成数字再加上100 console.log(sum.toFixed(2))//223.46 //表示结果取两位小数
Math.random():随机数
//0-10 不包含10 var res = Math.floor(Math.random() * 10) //0-10 包含10 var res = Math.floor(Math.random() * (10 + 1)) //10-20 不包含20 var res = Math.floor(Math.random() * 10) + 10 //10-20 包含20 var res = Math.floor(Math.random() * (10 + 1)) + 10
Math.round():四舍五入取整
Math.ceil():向上取整
Math.floor():向下取整
Math.abs():绝对值
Math.pow():幂
Math.sqrt():开方
Math.max():最大值
Math.min():最小值
Math.PI:圆周率
Math.E:自然对数的底数
8.时间对象
8-1创建时间对象
//不传参数,表示当前的北京时间
var date = new Date()
console.log(date);//自动转为字符串
//new Date传参
//1个参数 毫秒数
var date1 = new Date(1000)
console.log(date1);//1970-1-1 08:00:01
//都是从1970年1月1日开始计算的,因为中国是东八区,所以是8小时
//2个参数 年 月
var date2 = new Date(2020,10)//月份是从0开始的
console.log(date2);//2020-11-01 08:00:00
//3个参数 年 月 日
var date3 = new Date(2020,10,10)
console.log(date3);//2020-11-10 08:00:00
//后面的时 分 秒依次类推
//new Date传字符串
var date8 = new Date('2020-11-10 10:10:10')
console.log(date8);//2020-11-10 10:10:10
var date9 = new Date('2020/11/10 10:10:10')
console.log(date9);//2020-11-10 10:10:10
8-2 时间对象的常用方法
getFullYear() 获取年份
getMonth():获取月份
返回的数字是0-11 ,对应的月份是1-12月
getDate():获取日期
getDay():获取星期
周日对应的是0 周一到周六:1-6
getHours():获取小时
getMinutes():获取分钟
getSeconds():获取秒
getMilliseconds():获取毫秒
getTime():获取时间戳
是距离1970-1-1 00:00:00的毫秒数,本身不是, 返回的是一串随机数
//时间戳转换为时间对象
var date10 = new Date(1605070610100)
setFullYear():设置年份
setMonth():设置月份
setDate():设置日期
setHours():设置小时
setMinutes():设置分钟
setSeconds():设置秒
setMilliseconds():设置毫秒
setTime():设置时间戳
8-3 倒计时计时器
语法:
setTimeout(需要执行的函数,多长时间后执行)
var timerId = setTimeout(function() { console.log("我执行了") } , 1000) console.log(timerId) //1 clearTimeout(timerId) //表示清除计时器timerId
- 时间是按照毫秒计算的,1000毫秒就是一秒钟
- 所以会在页面打开一秒钟以后执行函数
- 只执行一次,之后就不执行了
- 返回值是当前这个定时器是页面中第几个定时器
8-4 间隔定时器
- 语法:
setInterval(需要执行的函数,每隔多长时间执行)
- 跟上面类似,只是会每隔一段时间执行这个函数
- 清除计时器用
clearInterval(返回值)
二、BOM
BOM
:浏览器对象模型- 就是一些操作浏览器的一些能力
- 我们可以操作的内容:
- 获取一些浏览器的相关信息(窗口的大小)
- 操作浏览器进行页面跳转
- 获取当前浏览器地址栏的信息
- 操作浏览器的滚动条
- 浏览器的信息(浏览器的版本)
- 让浏览器出现一个弹出框(
alert/confirm/prompt
)
BOM
的核心就是window
对象window
是浏览器内置的一个对象,里面包含了操作浏览器的方法
1. 获取浏览器窗口的尺寸
innerHeight
和innerWidth
这两个方法分别是用来获取浏览器窗口的高度和宽度(包含滚动条)
//获取浏览器的高度 //window可以省略 var windowHeight = window.innerHeight console.log(windowHeight) //获取浏览器的宽度 var windowWidth = window.innerWidth console.log(windowWidth)
2. 浏览器的弹出层
- alert:弹出一个警告框
- confirm:弹出一个确认框
- prompt:弹出一个提示框
3.浏览器的地址信息
- location.href:跳转页面
- location.reload():刷新页面
4. 浏览器的常见事件
window.onload:页面加载完成后触发
window.onresize:页面大小发生改变时触发
window.onscroll:页面滚动时触发
window.document.documentElement.scrollTop和window.document.body.scrollTop:都是测量离顶部的距离(window可以省略)
window.scrollTo() :
- window.scrollTo(0 , 0) :表示回到(0,0)这个位置
- window.scrollTo({left:0 , top:0}) :也表示回到顶部
window.open(“网址”):在一个新的页面打开,原来的页面不变
window.close():只能是关闭自己的页面
5. 浏览器的历史记录
window
中有一个对象叫做history
- 是专门用来存储历史记录信息的
history.back
history.back
是用来回退历史记录的,就是回到前一个页面window.history.back()
- 前提是你要有删一条记录,不然就是一直在这个页面,也不会回退
history.forword
history.forword
是去到下一个历史记录里面,也就是去到下一个页面window.history.forword()
- 前提是你要之前有过回退操作,不然的话你现在就是最后一个页面,没有下一个
6. 浏览器本地存储
6-1 loclocalStorage:永久存储
//增
localStorage.setItem("age" , "14")
//存数据, 两个参数, 第一个是键, 第二个是值
//只能存储字符串,如果要存储对象,那么可以使用`JSON`字符串
//如果之前的键已经存在,那么会覆盖原来的值,也就是修改
//取
localStorage.getItem("age")
//删
localStorage.removeItem("age")
//清空
localStorage.clear()
6-2 sessionStorage:临时存储
//增
sessionStorage.setItem("age" , "14")
//取
sessionStorage.getItem("age")
//删
sessionStorage.removeItem("age")
//清空
sessionStorage.clear()
sessionStorage:关闭页面数据就会消失
三、DOM
DOM(Document Object Model)
:文档对象模型- 其实就是操作
html
中标签的一些能力 - 我们可以操作哪些内容
- 获取一个元素
- 移除一个元素
- 创建一个元素
- 向页面里面添加一个元素
- 给元素绑定一些事件
- 获取元素的属性
- 给元素添加一些
css
样式 - …
DOM
的核心对象就是document
对象document
对象是浏览器内置的一个对象,里面存储着专门用来操作元素的各种方法DOM
:页面中的标签,我们通过js
获取以后,就把这个对象叫做DOM对象
1. 获取一个元素
document.documentElement:rem,获取的就是html
document.head:获取head
document.body:获取body
getElementById:根据id获取元素
getElementsByClassName:根据class获取元素
获取到的是一个伪数组,可以使用Array.from()将这个伪数组转为真正的数组
getElementsByTagName:根据标签名获取元素
他会将这个代码里面的所有的这个标签都获取下来
getElementsByName:根据取得的元素的name属性值获取元素
querySelector:根据选择器获取元素
//都是根据之前写style样式的方式获取 var items = document.querySelector(".newItem") var items = document.querySelector("#box") //注意:这个只能获取第一次出现的对象 //下面那个就可以获取所有的指定对象
querySelectorAll:根据选择器获取元素
2. 操作元素属性
2-1 操作原生属性
<body>
<div id = "box">hello</div>
<input type = "text" id = "password">
<script>
box.innerHTML = "world" //这个会将id为box的div里面的内容改为"world"
password.type = "password" //这个会将id为password的文本框改为密码框
</script>
</body>
注意:这些都是通过
id
来修改里面的属性
2-2 自定义属性
<body>
<ul>
<li>111</li>
<li>222</li>
<li>333</li>
<li>444</li>
</ul>
<div id = "box" nb = "222"></div>
<script>
//自定义属性有两种方式
//第一种
//setAttribute(设置属性)、getAttribute(获取属性的值)、removeAttribute(删除属性)
var itmes = document.getElementsByTagName("li")
for (var i = 0 ; i < itmes.length ; i++) {
itmes[i].setAttribute("index" , i)//这样给每个li的标签都添加了一个名为"index"的属性,并且值为"i"
}
//第二种方法(比较推荐,主流,主要是为了方便区分自己定义的属性和原生属性)
//他会以"data-*****"的方式出现,便于区分
var oitmes = document.getElementsByTagName("li")
for (var i = 0 ; i < oitmes.length ; i++) {
oitmes[i].dataset.index = i //这样给每个li的标签都添加了一个名为"data-index"的属性,并且值为"i"
}
//想要删除这个属性的话用下面的方法
delete box.dataset.nb
</script>
</body>
注意:一般都是用下面的方法,比较常用
3. 操作元素文本内容
innerHTML
:获取元素内部的内容,包括标签innerText
:获取元素内部的内容,不包括标签value
:获取表单元素的值
4. 操作元素样式
行内元素
//只能行内样式方法,style --读写 box.style.width = 100px console.log(box.style["background-color"]) //不能直接"."要使用[] console.log(box.style.backgroundColor) //或者使用驼峰命名法
内部样式,外部样式
//内部样式,外部样式,行内getComputedStyle 获取,不能赋值写样式 var obox = document.getElementById("box") var res = getComputedStyle(obox)["background-color"] var res = getComputedStyle(obox).backgroundColor
注意:只能获取,不能修改样式,但是上面那种方式可以修改样式
5. 操作元素类名
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.item{
width: 100px;
height: 100px;
background-color: red;
color: black;
border-radius: 10px;
}
</style>
</head>
<body>
<div id = "box" class = "item item1 item2">hello world</div>
<button id = "btn">click</button>
<script>
//.className 可以获取到元素的所有样式
//.className 可以设置元素的样式
console.log(box.className)
box.className = "item item1"//这样会覆盖前面的样式
box.className += " item2"//注意要有空格,否则会跟前面的样式合并
//classList 是一个类数组对象,里面存放着元素的所有样式
console.log(box.classList)
box.classList.add("item3")//添加样式
box.classList.remove("item1")//移除样式
// box.classList.toggle("item")//切换样式,如果有就移除,如果没有就添加
btn.onclick = function(){
box.classList.toggle("item")
}
</script>
</body>
</html>
6. DOM节点
DOM
的节点一般分为常用的三大类元素节点、文本节点、属性节点- 什么是分类,比如我们在获取元素的时候,通过各种方法获取到的我们叫做元素节点(标签节点)
- 比如我们标签里面写的文字,那么就是文本节点
- 写在每一个标签上的属性,就是属性节点
6-1 元素节点
- 我们通过
getElement...
获取到的都是元素节点
6-2 属性节点
- 我们通过
getAttribute
获取到的就是属性节点
6-3 文本节点
- 我们通过
innerText
获取到的就是元素的文本节点
6-4 获取节点的方式
childNodes
和children
childNodes属性返回指定节点的子节点集合
children属性返回指定节点的子元素集合
firstChild
和firstElementChild
firstChild属性返回指定节点的第一个子节点
firstElementChild属性返回指定节点的第一个子元素lastChild
和lastElementChild
lastChild属性返回指定节点的最后一个子节点
lastElementChild属性返回指定节点的最后一个子元素previousSibling
和previousElementSibling
previousSibling属性返回指定节点的前一个兄弟节点
previousElementSibling属性返回指定节点的前一个兄弟元素nextSibling
和nextElementSibling
nextSibling属性返回指定节点的后一个兄弟节点
nextElementSibling属性返回指定节点的后一个兄弟元素parentNode
和parentElement
parentNode属性返回指定节点的父节点
parentElement属性返回指定节点的父元素
<body>
<div id="box">
kerWin
<p>111</p>
<!-- 我是注释 -->
</div>
<script>
// 1.\n
// kerWin
// \n
// 2.<p>111</p>
// 3.\n
// 4.<!-- 我是注释 -->
// 5.\n
//childNodes属性 vs children属性
//childNodes属性返回指定节点的子节点集合
//children属性返回指定节点的子元素集合
console.log(box.childNodes)//返回所有节点
console.log(box.children)//返回所有元素
//firstChild vs firstElementChild
//firstChild属性返回指定节点的第一个子节点
//firstElementChild属性返回指定节点的第一个子元素
console.log(box.firstChild)//返回第一个节点
console.log(box.firstElementChild)//返回第一个元素
//lastChild vs lastElementChild
//lastChild属性返回指定节点的最后一个子节点
//lastElementChild属性返回指定节点的最后一个子元素
console.log(box.lastChild)//返回最后一个节点
console.log(box.lastElementChild)//返回最后一个元素
//previousSibling vs previousElementSibling
//previousSibling属性返回指定节点的前一个兄弟节点
//previousElementSibling属性返回指定节点的前一个兄弟元素
console.log(box.previousSibling)//返回前一个兄弟节点
console.log(box.previousElementSibling)//返回前一个兄弟元素
//nextSibling vs nextElementSibling
//nextSibling属性返回指定节点的后一个兄弟节点
//nextElementSibling属性返回指定节点的后一个兄弟元素
console.log(box.nextSibling)//返回后一个兄弟元素
console.log(box.nextElementSibling)//返回后一个兄弟元素
//parentNode vs parentElement
//parentNode属性返回指定节点的父节点
//parentElement属性返回指定节点的父元素
console.log(box.parentNode)//返回父节点
console.log(box.parentElement)//返回父元素
</script>
</script>
</body>
</html>
6-5 节点操作
创建节点
//createElement("节点"):创建节点 var odiv = document.createElement("div") //可以对节点进行任意操作 odiv.className = "aaa" odiv.id = "aaa"
插入节点
//appendChild()方法将一个节点添加到指定父节点的子节点列表的末尾 box.appendChild(odiv) //box.insertBefore(要插入的节点,谁的前面) //表示在box里面的"谁的前面"插入一个节点 box.insertBefore(odiv , box1)
删除节点
//删除节点 box.removeChild(box1) //删除自己以及自己的后代 box.remove()
替换节点
//replaceChild()方法用一个新节点替换指定父节点的一个子节点 //replaceChild(新节点,指定节点) box.replaceChild(obox,box1)
复制节点
//cloneNode()方法复制一个节点 //cloneNode(true)复制节点及其所有子节点 var obox2 = obox.cloneNode(true) box.appendChild(obox2)
6-6 节点属性
nodeType | nodeName | nodeValue | |
---|---|---|---|
元素节点 | 1 | 大写标签名 | null |
属性节点 | 2 | 属性名 | 属性值 |
文本节点 | 3 | #text | 文本内容 |
6-7 获取元素尺寸
- offsetWith和offsetHeight
offsetWith
:获取的元素内容 + padding + border的宽度offsetHeight
:获取的元素内容 + padding + border的高度
- clientWidth和clienHeight
clienWidth
:获取的是元素内容 + padding的宽度clientHeight
:获取的是元素内容 + padding的高度
注意:
- 获取到的尺寸是没有单位的数字
- 当元素在页面中不占未知的时候,获取到的是
0
6-8 获取元素偏移量
offsetLeft和offsetTop
offsetLeft
:获取的是元素距离左侧的距离offsetTop
:获取的是元素距离顶部的距离
注意:
参考点: 是定位父级
如果父级元素都没有定位,偏移量相对于body
clientLeft和clientTop
clientLeft
:距离左边框的距离clienTop
:距离上边框的距离
6-9 获取可视窗口的尺寸
innerWidth和innerHeight
document.documentElement.clientWidth和document.documentElement.clientHeight
注意:
第一个:访问的尺寸含有滚动条的宽度
第二个:访问的尺寸不包含滚动条的宽度
7. 事件
一个事件由什么东西组成
- 触发谁的事件:事件源
- 触发什么事件:事件类型
- 触发以后做什么:事件处理函数
var odiv = document.querySelector("div") odiv.onclick = function() { console.log("你点击了div") } //谁来触发事件 => odiv => 这个事件的事件源就是odiv //触发什么事件 => onclick => 这个事件类型就是click //触发之后做什么 => function(){} => 这个事件的处理函数 //每次点击div的时候都会调用里面的函数
7-1 初识事件
dom0 类型 --后面会覆盖前面的
box.onclick = function() { alert("1111") }
dom2 类型 – 绑定多个事件处理函数,按照顺序执行
box2.addEventListener("click",function() { alert("1111") }) box2.addEventListener("click",function() { alert("2222") })
7-2 事件解绑
dom0 类型 --dom节点.onclick = null
box.onclick = null
dom2 类型 --dom节点.removeEventListener(“click”,function() {})
function handler() { console.log("1111") //解绑事件 this.removeEventListener("click", handler) } //事件触发 btn.addEventListener("click" , handler)
7-3 事件类型
- 鼠标事件
click
:单击执行dblclick
:双击执行contextmenu
:右键菜单事件mousedown
:鼠标按下事件mouseup
:鼠标抬起事件mousemove
:鼠标移动事件mouseenter
:鼠标移入事件(在孩子节点里面移入移出不会触发)mouseleave
:鼠标移出事件(在孩子节点里面移入移出不会触发)mouseover
:鼠标移入事件(在孩子节点里面移入移出会触发)mouseout
:鼠标移出事件(在孩子节点里面移入移出会触发)
- 键盘事件
keydown
:键盘按下事件keyup
:键盘抬起事件keypress
:键盘按下事件
- input事件
focus
:获取焦点事件blur
:失去焦点事件change
:改变事件,当获取焦点和失去焦点的时候里面的内容不一样才会触发input
:输入事件,输入内容不一样就触发select
:选择事件submit
:提交事件reset
:重置事件
- 触摸事件(只对移动端有效)
touchstart
:触摸开始事件touchend
:触摸结束事件touchmove
:触摸移动事件touchcancel
:触摸取消事件
7-4 事件对象
浏览器给了我们一个黑盒子,叫做
window.event
,就是对事件信息的所有描述比如点击事件
你点在了
(0,0)
位置,那么你得到的这个事件对象里面对应的就会有这个点位的属性你点在了
(10,10)
位置,那么你得到的这个事件对象里面对应的就会有这个点位的属性odiv.onclick = function() { console.log(window.event.x轴坐标信息) console.log(window.event.y轴坐标信息) }
鼠标事件
clientX,clientY:距离浏览器可视窗口的左上角的坐标值
pageX,pageY:距离页面文档流的左上角的坐标值
offsetX,offsetY:距离出发元素的左上角的坐标值
注意:如果当前元素里面还有子元素,那么点击这个子元素也会有值,只是这个值是距离自己子元素左上的值
stopPropagation:阻止事件传播
fanction fn(evt) { evt.stopPropagation }
阻止默认行为
document.oncontextmenu = function() { console.log("右键单击") return false }
7-5 事件委托
- 就是把我要做的事情委托给别人来做
- 因为我们的冒泡机制,点击子元素的时候,也会同步触发父元素的相同事件
- 所以我们就可以把子元素的事件委托给父元素来做
事件触发
点击子元素的时候,不管子元素有没有点击事件,只要父元素有点击事件,那么就可以触发父元素的点击事件
<body> <ul> <li>1</li> <li>2</li> <li>3</li> </ul> <script> var oul = document.querySelector("ul") oul.addEventListener("click" , function(e) { console.log("我是ul的点击事件,我被触发了") }) </script> </body>
- 像上面的代码,当你点击ul的时候肯定会被触发
- 但是当你点击li的时候,也会触发
target
target
这个属性是事件对象里面的属性,表示你点击的目标当你触发点击事件的时候,你点击在哪个元素上,
target
就是哪个元素这个
target
也不兼容,在IE
下要使用srcElement
<body> <ul> <li>1</li> <li>2</li> <li>3</li> </ul> <script> var oul = document.querySelector("ul") oul.addEventListener("click" , function(e) { e = e || window.event var target = e.target || e.srcElement console.log(target) }) </script> </body>
- 上面的代码,当你点击
ul
的时候,target
就是ul
- 当你点击在
li
上面的时候,target
就是li
- 上面的代码,当你点击
8. 正则表达式
字面量 //
var reg1 = /abc/;
**构造函数 **
var reg2 = new RegExp('abc');
例子:
<script> // 正则表达式 //js复杂类型 // 1. 字面量 // var reg1 = /abc/; console.log(reg1); // 2. 构造函数 var reg2 = new RegExp('abc'); console.log(reg2); //失去焦点时触发onblur mytext.onblur = function() { console.log(mytext.value); //test : 测试是否匹配 console.log(reg1.test(mytext.value)); } </script>
8-1 元字符
\d
: 匹配一个数字字符。等价于 [0-9]。\d\d
:表示就要包含两个数字字符里面的内容可以是字符串数字,也可以是数字
\D
:匹配一个非数字字符。等价于 ![0-9]。\s
:匹配一个空白字符,包括空格、缩进 、制表符、换页符和换行符。\S
:匹配一个非空白字符。等价于 ![\s]。\w
:匹配包括下划线的任何单词字符。等价于’[A-Z、a-z、0-9、_ ]'。\W
:匹配任何非单词字符。等价于 ‘![A-Za-z0-9_]’。.
: 匹配任意内容(换行符不算)注意:要全是换行符才不算,如:
\n\n\n
,返回false如果是
ab\ncd
,返回的还是true\
:转义字符像上面那个如果写成
\.
就表示一个普通的.
,而不是元字符
<body>
<script>
// 1. \d :匹配一个数字字符。等价于 [0-9]。
var reg = /\d/
console.log(reg.test('1')) // true
console.log(reg.test('a')) // false
console.log(reg.test('1a')) // true
console.log(reg.test('a1')) // true
// 2. \D :匹配一个非数字字符。等价于 [^0-9]。
var reg = /\D/
console.log(reg.test('1')) // false
console.log(reg.test('a')) // true
// 3. \s :匹配一个空白字符,包括空格、制表符、换页符和换行符。
var reg = /\s/
console.log(reg.test(' ')) // true
console.log(reg.test('\t')) // true
// 4. \S :匹配一个非空白字符。等价于 [^\s]。
var reg = /\S/
console.log(reg.test(' ')) // false
console.log(reg.test('a')) // true
// 5. \w :匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。
var reg = /\w/
console.log(reg.test('a')) // true
console.log(reg.test('1')) // true
// 6. \W :匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。
var reg = /\W/
console.log(reg.test('a')) // false
console.log(reg.test('1')) // false
// 7. . :匹配任意内容(换行符不算)
var reg = /zo*/
console.log(reg.test('z')) // true
console.log(reg.test('zo')) // true
// 8. \ :转义字符
var reg = /\./
console.log(reg.test('.')) // true
console.log(reg.test('a')) // false
</script>
</body>
8-2 边界符
^
:匹配输入字符串的开始位置。$
:匹配输入字符串的结束位置。
<body>
<script>
// 1. ^ :匹配输入字符串的开始位置。
var reg = /^\d/
console.log(reg.test('aab')) // false
console.log(reg.test('1b')) // true
// 2. $ :匹配输入字符串的结束位置。
var reg = /\d$/
console.log(reg.test('aab')) // false
console.log(reg.test('a1')) // true
// 3. ^开头结尾$
var reg = /^\d$/
console.log(reg.test('aab')) // false
console.log(reg.test('1')) // true
</script>
</body>
8-3 限定符
*
:匹配前面的子表达式零次或多次。+
:匹配前面的子表达式一次或多次。?
:匹配前面的子表达式零次或一次。{n}
:n 是一个非负整数。匹配确定的 n 次。{n,}
:n 是一个非负整数。至少匹配n 次。{n,m}
:最少匹配 n 次且最多匹配 m 次。注意:他只能修饰他前面的一个字符,对于再前面的,表示固定的
<body>
<script>
// 1. * :匹配前面的子表达式零次或多次。
var reg = /zo*/
console.log(reg.test('z')) // true
console.log(reg.test('zo')) // true
// 2. + :匹配前面的子表达式一次或多次。
var reg = /zo+/
console.log(reg.test('z')) // false
console.log(reg.test('zo')) // true
console.log(reg.test('o')) // false
// 3. ? :匹配前面的子表达式零次或一次。
var reg = /zo?/
console.log(reg.test('z')) // true
console.log(reg.test("zozo")) // true
console.log(reg.test("zo")) // true
console.log(reg.test("o")) // false
// 4. {n} :n 是一个非负整数。匹配确定的 n 次。
var reg = /zo{2}/
console.log(reg.test('z')) // false
console.log(reg.test("zo")) // false
console.log(reg.test("zoo")) // true
console.log(reg.test("zooo")) // true
// 5. {n,} :n 是一个非负整数。至少匹配n 次。
var reg = /zo{2,}/
console.log(reg.test('z')) // false
console.log(reg.test("zo")) // false
console.log(reg.test("zoo")) // true
// 6. {n,m} :最少匹配 n 次且最多匹配 m 次。
var reg = /zo{2,3}/
console.log(reg.test('z')) // false
console.log(reg.test("zo")) // false
</script>
</body>
8-4 特殊符号
()
:整体|
:或注意:他表示是他左右两边的整体字符,不是一个
var reg = /abc | ddd/
表示含有abc或者ddd字符串
[]
:字符集。匹配包含的任一字符。[0-9]:表示匹配数字0、1、2、3、4、5、6、7、8、9中的任意一个字符
[^]
:排除字符集。匹配未包含的任何字符。
<body>
<script>
// 1. () :匹配括号内的表达式,也表示一个组。
var reg = /(abc){2}/
console.log(reg.test('abc')) // false
console.log(reg.test('abcabc')) // true
// 2. | :表示或。
var reg = /abc|cba/
console.log(reg.test('abc')) // true
console.log(reg.test('cba')) // true
console.log(reg.test('ab')) // false
// 3. [] :字符集。匹配包含的任一字符。
var reg = /[abc]/
console.log(reg.test('a')) // true
console.log(reg.test('b')) // true
console.log(reg.test('d')) // false
// 4. [^] :排除字符集。匹配未包含的任何字符。
var reg = /[^abc]/
console.log(reg.test('a')) // false
console.log(reg.test('b')) // false
console.log(reg.test('d')) // true
</script>
</body>
8-5 捕获exec
语法:reg.exec(“字符串”)
标识符:(他是写在正则表达式的外面的,如:/\d/g
)
g
:表示全局匹配。i
:表示不区分大小写。m
:表示多行匹配。u
:表示使用unicode码的模式进行匹配。y
:表示粘性匹配。
8-6 正则与字符串方法
字符串.replace()
: 替换与正则表达式匹配的子串。var str = 'asdfadafdsaf' //正则表达式后面加了一个"g",表示所有的,全局的 var newstr = str.replace(/a/g , "*") console.log(newstr) //*sdf*d*fds*f
字符串.search()
: 检索与正则表达式相匹配的值。不管是不是全局都只能检索到第一个出现的字符
var str = 'asdfadafdsaf' var newstr = str.search(/a/g) //如果是表达式里面没出现的就是-1 console.log(newstr) //0 console.log(str.search(/ax/)) //-1
字符串.match()
: 找到一个或多个正则表达式的匹配。var datestr = "time is from 2029-01-01 to 2029-12-31" console.log(datestr.match(/\d{4}-\d{1,2}-\d{1,2}/g)) //["2029-01-01", "2029-12-31"]
9. this指向
改变this指向:
call
:执行函数,并改变this指向为函数的第一个参数注意:支持多个参数
apply
:执行函数,并改变this指向为函数的第一个参数注意:只支持两个参数,第二个参数是一个数组
bind
:改变this指向为函数的第一个参数,不会自动执行函数注意:支持多个参数,还要手动执行
<body>
<button id="btn">click</button>
<script>
// call apply bind
var name = "111"
var obj1 = {
name : "obj1",
getName :function(a , b ,c) {
console.log("getName1" , this.name)
console.log("参数" , a, b, c)
}
}
var obj2 = {
name : "obj2",
getName :function(a , b ,c) {
console.log("getName2" , this.name)
console.log("参数" , a, b, c)
}
}
// call 方法
// 语法:函数.call(谁调用我,参数1,参数2,参数3,...)
// 作用:改变函数的this指向
// 返回值:函数的返回值
obj1.getName.call(obj2, 1, 2, 3)
// apply 方法
// 语法:函数.apply(谁调用我,[参数1,参数2,参数3,...])
// 作用:改变函数的this指向
// 返回值:函数的返回值
obj1.getName.apply(obj2, [1, 2, 3])
// bind 方法
// 语法:函数.bind(谁调用我,参数1,参数2,参数3,...)
// 作用:改变函数的this指向
// 返回值:函数
var fun1 = obj1.getName.bind(obj2, 1, 2, 3)
console.log(fun1)//函数
fun1()//手动执行
btn.onclick = handler.bind(window)
function handler() {
console.log(1111 , this)
}
</script>
</body>
四、ES6
1. ES6定义变量
let和const关键字
let
和const
与var
的区别:let
和const
不允许重复声明变量let
和const
必须先声明定义才可以使用let
和const
的块级作用域只要是{}
里面的都是,而var
只有函数里面才是
let
和const
的区别:let
可以只声明不赋值,而const
必须要赋值,const
是定义一个常量,是不能更改的- 注意:
const
定义一个对象,然后改变对象里面的元素内容是可以的,因为堆栈问题,cosnt
只是存储的一个地址,地址并没有改变,对象里面的内容是存在堆里面
2. ES6的箭头函数
语法:
var test = () => {}
可以省略
function
,变成=>
只有一个参数时,可以省略
()
函数体只有一句话或者只有返回值的时候,可以省略
{}
函数体只有一句
return
时,可以省略return
注意如果返回的是一个对象类型的时候,省略了
return
和{}
,要在对象的{}
的外面加上一个()
,防止把对象的{}
当成了函数的没有
arguments
arguments:是函数里面的一个伪数组,当你定义函数而没有设置形参的时候,而你调用函数时,传入了参数,想要获取这个参数就可以用
arguments[0]、arguments[1]....
获取箭头函数没有
this
,或者说箭头函数的this
是父级作用域的
3. ES6的解构赋值
快速的从对象和数组中获取里面的成员
var arr = ['a' , 'b' , 'c'] //注意只能是按照顺序取出 let [x , y , z] = arr console.log(x , y , z) // a b c //快速换值 var a = 10 var b = 20 var [a , b] = [b , a]//这样a=20,b=10 var obj = { name : 'zhangsan', age : 20 , location : 'jiangxi' } //这里要一一对应,不然拿不出来,前面的key要一样 //这里最后的location:mylocation就是重命名,将location的名字改为mylocation,那之前的location就不能调用了 let {name , age , location:mylocation} = obj console.log(name , age , mylocation) //这里就不能用location了,因为没有被定义 //复杂对象类型 var obj = { name: '张三', age: 18, location: { city: '北京', province: '北京' }, hobbies: ['唱', '跳', 'rap'] } var {name, age, location: {city, province}, hobbies: [hobby1, hobby2, hobby3]} = obj console.log(name, age, city, province, hobby1, hobby2, hobby3);//张三 18 北京 北京 唱 跳 rap
4. ES6的展开运算符
...
:展开运算符(不管是数组还是对象都可以展开)在对象里面,如果key和value一样可以省略value,比如:{name:name};这样可以简写成{name}
var a = [1 , 2 , 3] var b = [4 , 5 , 6] var c = [...a , ...b] console.log(c)//[1, 2, 3, 4, 5, 6] var obj1 = { name : '张三' , age : 18 , sex : '男' } //只是展开obj1里面的内容存到obj2中,如果有的内容一样那么就会被覆盖 var obj2 = { ...obj1 , name : '李四' , hobby : '唱歌' } console.log(obj2);//{name: "李四", age: 18, sex: "男", hobby: "唱歌"}
5. ES6模块化语法
export导出
// 方式1:声明时导出 export const name = 'Alex'; export function sum(a, b) { return a + b; } // 方式2:统一导出 const PI = 3.14; const multiply = (x, y) => x * y; export { PI, multiply }; // 方式3:重命名导出(用as重命名可以避免不同js里面的函数或者变量名相同的情况) export { PI as π, multiply as mul }; // 一个模块只能有一个默认导出 export default class Calculator { // ... } // 或 const utils = { /* ... */ }; export default utils;
import导入
//1. 导入命名导出 import { sum, PI } from './math.js'; import { sum as add, PI as π } from './math.js'; // 重命名 import * as math from './math.js'; // 整体导入 //2.导入默认导出 import Calculator from './calculator.js'; import calc from './calculator.js'; // 任意命名
注意:
文件扩展名:浏览器中需明确写
.js
扩展名路径规范:
- 相对路径:
./
或../
- 绝对路径:以
/
开头- 第三方模块:直接写包名(需配置)
严格模式:模块自动启用严格模式
浏览器使用:需添加
type="module"
<script type="module" src="app.js"></script>
跨域限制:需配置CORS或使用本地服务器
五、面向对象
1. 对象基础
1-1 创建对象
// 字面量创建
const obj = {
name: 'John',
sayHi() {
console.log(`Hi, ${this.name}`);
}
};
// new Object() 创建
const obj2 = new Object();
obj2.age = 30;
1-2 属性访问
// 点语法
console.log(obj.name);
// 方括号语法
console.log(obj['name']);
2. 构造函数
2-1 基本用法
function Person(name, age) {
this.name = name;
this.age = age;
this.introduce = function() {
console.log(`I'm ${this.name}, ${age} years old`);
};
}
const p1 = new Person('Alice', 25);
2-2 new关键字的作用
- 创建新空对象
- 绑定this到新对象
- 自动返回新对象
2-3 注意事项
- 首字母要大写
- 构造函数不写
return
- 构造函数不能当成普通函数使用
3. 原型(Prototype)
3-1 原型链机制
function Animal(name) {
this.name = name;
}
//用prototype这个可以节省空间
Animal.prototype.eat = function() {
console.log(`${this.name} is eating`);
};
const cat = new Animal('Mimi');
cat.eat(); // 通过原型链查找方法
//对象.__proto__ === 构造函数.prototype
3-2 原型相关方法
// 检查原型
console.log(Object.getPrototypeOf(cat) === Animal.prototype);
// 设置原型
const dog = { name: 'Buddy' };
Object.setPrototypeOf(dog, Animal.prototype);
4. ES6 Class
4-1 类定义
class Rectangle {
//构造器函数
constructor(height, width) {
this.height = height;
this.width = width;
}
// 方法
get area() {
return this.calcArea();
}
calcArea() {
return this.height * this.width;
}
// 静态方法
static info() {
console.log('This is a Rectangle class');
}
}
4-2 继承
class Square extends Rectangle {
constructor(side) {
super(side, side);
}
}
5. 继承
5-1 构造函数继承
function student(name , age , classroom) {
//这里就是属性继承,方法没法继承
Person.call(this , name , age)
this.classroom = classroom
}
5-2 原型链继承
function Parent() {
this.name = 'Parent';
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
function Child() {}
Child.prototype = new Parent();
const child = new Child();
5-3 组合继承(经典继承)
function Parent(name) {
this.name = name;
}
function Child(name, age) {
Parent.call(this, name); // 继承属性
this.age = age;
}
//方法继承
//Child.prototype = Object.create(Parent.prototype);
//Child.prototype.constructor = Child;
Child.prototype = new Parent()
5-4 ES6类继承
class Vehicle {
constructor(wheels) {
this.wheels = wheels;
}
say() {
console.log('aa')
}
}
//extends继承
class Car extends Vehicle {
constructor(wheels, brand) {
//super继承父类里面的元素
super(wheels);
this.brand = brand;
}
//覆盖
say() {
//super.say()//调用父类方法
console.log('bb')
}
}
六、ajax
核心作用:在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页内容
主要特点:
- 异步通信
- 基于现有的 Web 标准(HTML, CSS, JavaScript, XML/JSON)
- 提升用户体验
传统
Web
vsAJAX
应用传统 Web 应用 AJAX 应用 同步请求(阻塞) 异步请求(非阻塞) 整页刷新 局部更新 频繁的服务器请求 按需获取数据
1. 基本用法
1-1 创建 XMLHttpRequest
对象
let xhr = new XMLHttpRequest(); // 现代浏览器
// 兼容旧版 IE(<= IE6):
// let xhr = new ActiveXObject("Microsoft.XMLHTTP");
1-2 配置请求
xhr.open(method, url, async);
// method: "GET", "POST" 等
// url: 请求地址
// async: 是否异步(默认 true:异步,false:同步)
注意:如果是
true
,表示异步可以后面的代码执行了在调用onreadystatechange
函数,但是如果是false
,如果后面的代码已经执行了,那么onreadystatechange
函数就不会执行了,除非将这个函数放在xhr.send()
的前面才会调用这个函数再执行后面的代码异步效率更高,一般都是异步,用
true
1-3 发送请求
/*
get:偏向获取数据
post:偏向提交数据
put:偏向更新数据(全部)
delete:偏向删除信息
patch:偏向部分修改
主要还是前面两个
*/
xhr.send(); // GET 请求
xhr.send(data); // POST 请求(可发送表单数据等)
1-4 处理响应
//注册事件,接受服务器返回的数据
xhr.onreadystatechange = function() {
//readyState 0-4:请求未发送 1:请求已发送 2:请求已接收 3:请求处理中4:请求已完成
//status 200-300
//status 404:请求的资源不存在 500:服务器内部错误
if (xhr.readyState === 4 && xhr.status === 200) {
// 处理响应数据
console.log(xhr.responseText); // 文本格式
console.log(xhr.responseXML); // XML 格式
}
};
//这个不用判断readyState是否为4,因为他已经要是4才能执行这个函数
xhr.onload = function(){
if(xhr.status == 200) {
console.log(xhr.responseText)
} else if (xhr.status == 404) {
console.error('请求的资源不存在')
} else if (xhr.status == 500) {
console.error('服务器内部错误')}
}
常见的HTTP状态码
- 200: 成功
- 404: 未找到页面
- 500: 服务器内部错误
2. 封装ajax
//将一个对象函数转化为查询字符串如:a = 1 & b = 2 & c = 3这种形式
function queryStringify(obj) {
let str = ''
for (let k in obj) str += `${k}=${obj[k]}&`
return str.slice(0, -1)
}
//封装ajax
function ajax(options) {
let defaultoptions = {
url : '',
method : 'get' ,
async : true,
data : {} ,
headers : {
'Content-Type' : 'application/json'
} ,
success : function () {},
error : function () {}
}
//将传入的参数和默认参数合并
let {url , method, async, data, headers, success, error} = {
...defaultoptions,
...options
}
//判断请求方式和数据类型来修改data
//headers['Content-Type']?.indexOf('json') > -1:判断headers中是否有Content-Type并且值为json,这里面的问号表示可选链操作符,意思是如果headers中没有Content-Type,那么就不会执行后面的indexOf方法,不会报错
if(typeof data === 'object' && headers['Content-Type']?.indexOf('json') > -1 && method === 'post') {
data = JSON.stringify(data)
} else {
data = queryStringify(data)
}
//如果是get请求,那么就将数据拼接在url后面
if(/^get$/i.test(method) && data) url += `?${data}`
//发送请求
const xhr = new XMLHttpRequest()
xhr.open(method, url, async)
xhr.onload = function() {
if(!/^2\d{2}$/.test(xhr.status)) {
error(`错误状态码:${xhr.status}`) //如果状态码不是2开头的,就执行error函数
return
}
//执行解析
try {
let res = JSON.parse(xhr.responseText)
success(res)
} catch (err) {
error("解析失败!因为后端返回的结果不是json格式字符串")
}
}
//设置请求头内的信息
for (let k in headers) xhr.setRequestHeader(k, headers[k])
//发送请求
if(/^get$/i.test(method)) {
xhr.send()
} else {
xhr.send(data)
}
}
//promise封装ajax
function pajax(options) {
return new Promise((resolve , reject) => {
ajax({
...options,
success(res) {
resolve(res)
} ,
error(err) {
reject(err)
}
})
})
}
// export {ajax , pajax}
// export default ajax
七、Promise
promise
是一个ES6的语法,用来解决回调地狱的问题回调地狱:就是指回调函数里面嵌套了回调函数,一直嵌套下去,有很多层
1. Promise基本语法
// 回调地狱示例
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
// Promise 是一个构造函数,自己身上有all、reject、resolve 这几个眼熟的方法,原型上有then、catch等同样很眼熟的方法。
// 既然是构造函数,那就可以用new关键字生成实例,
// 而该构造函数接受一个参数,是函数,并且有两个参数,分别是resolve和reject。
// 分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。
// 实例生成后,可以用then方法分别指定res和rej回调函数。
let q = new Promise(function(resolve , reject){
// 异步任务
setTimeout(() => {
// 异步任务成功
resolve('成功的数据');
// 异步任务失败
// reject('失败的数据');
}, 1000);
})
//q是promise的对象
q.then((res) => {
//兑现承诺,执行这个函数
console.log(res);
}).catch((err) => {
//拒绝承诺,执行这个函数
console.log(err);
})
2. Promise的生命周期
- 一个 Promise 有以下三种状态:
状态 | 描述 |
---|---|
pending | 初始状态,既不是成功也不是失败 |
fulfilled | 操作成功完成 |
rejected | 操作失败 |
- 状态转换:
pending -> fulfilled
pending -> rejected
3. 链式调用
Promise 链的特点:
- 每个 then() 返回新 Promise
- 返回值会自动包装成 Promise
- 可以传递值到下一个 then()
let q = new Promise(function(resolve , reject) {
...
})
q.then(res => {
return pajax({})
}).then(res => {
return pajax({})
}).catch(err => {
console.log(...)
})
4.Promise静态方法
方法 | 描述 |
---|---|
Promise.all(iterable) | 所有成功时返回结果数组,任何一个失败立即拒绝 |
Promise.allSettled(iterable) | 所有 Promise 完成后返回结果数组(ES2020) |
Promise.race(iterable) | 采用第一个完成(成功/失败)的 Promise 结果 |
Promise.any(iterable) | 采用第一个成功的 Promise 结果(ES2021) |
Promise.resolve(value) | 返回已解决的 Promise |
Promise.reject(reason) | 返回已拒绝的 Promise |
示例:Promise.all
const promises = [
fetch('/api/data1'),
fetch('/api/data2'),
fetch('/api/data3')
];
Promise.all(promises)
.then(results => {
// results 是按顺序的结果数组
})
.catch(error => {
// 任一请求失败立即进入此处
});
5.Promise 与 async/await
async/await
是一个es7的语法语法:
async function fn() { const res = await promise对象 return res }
这个可以把异步代码写的看起来像同步代码
只要是一个promise对象,那么我们就可以用
async/await
来写只能影响
async
里面的代码,函数外面的无法影响如果需要在内部捕获错误可以用
try/catch
例子:
// Promise 链式调用
function getData() {
return fetchData()
.then(data => processData(data))
.then(result => saveResult(result))
.catch(handleError);
}
// async/await 版本
async function getData() {
try {
const data = await fetchData();
const processed = await processData(data);
return await saveResult(processed);
} catch (error) {
handleError(error);
}
}
6. Fetch
- Fetch API 提供了现代网络请求解决方案,替代传统的 XMLHttpRequest。
6-1 基本使用方法
GET
请求:默认是get请求fetch('https://api.example.com/data') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Error:', error));
POST
请求fetch('https://api.example.com/data', { method: 'POST', headers: { 'Content-Type': 'application/json', }, //注意这里要按照fetch的格式,是body传入数据,而不是用data //而且传入的数据格式要严格按照上面写的格式传入 body: JSON.stringify({ name: 'John', age: 30 }) });
6-2 响应对象属性
fetch(url).then(response => {
console.log(response.status); // HTTP 状态码
console.log(response.statusText); // 状态文本
console.log(response.headers); // 响应头
console.log(response.ok); // 状态码 200-299 时为 true
});
6-3 解析不同格式的响应
// JSON
response.json()
// 文本
response.text()
// Blob(二进制数据)
response.blob()
// FormData
response.formData()
// ArrayBuffer
response.arrayBuffer()
6-4 网络错误处理
fetch(url)
.then(res => {
if (res.ok) {
//这里拿不到本来的数据,要先返回json数据,然后到下一个then才能拿到数据,返回的是状态码
return res.json();
} else {
//拒绝请求
return Promise.reject({
status : res.status ,
statusText : res.statusText
})
}
})
.then(res => {
//这里才可以拿到数据
console.log(res)
})
.catch(error => {
console.error('Fetch error:', error);
});
7. jsonp
Jsonp
是json
的一种"使用模式",可以让网页从别的域名(网站)获取资料,即跨域读取数据。因为同源策略,才需要这个特殊的技术
const script = document.createElement('script') script.src = './kerwin.txt'//这里是要传的地址 document.body.appendChild(script)
jsonp
原理:动态创建script标签,src属性指向没有跨域限制,指向一个接口,接口返回的格式一定是***()函数表达式,函数的参数就是接口返回的数据注意:
后端接口形式必须***(),需要后端配合
jsonp缺点:
获取了数据之后删除
//因为它获取数据是每次都插入了一个script标签,然后获取数据,获取完数据之后就可以将这个标签删除了,直接用onload删除 script.onload = function () { script.remove() }
只能
get
请求,不能post
、put
、delete