参考资料:课程链接
目录
1.ES6
我们所说的ESS和ES6其实就是在js语法的发展过程中的一个版本而已
- ECMAScript 就是js的语法
- 以前的版本没有某些功能
- 在ES5这个版本的时候增加了一些功能
- 在ES6这个版本的时候增加了一些功能
- 因为浏览器是浏览器商生产的
- ECMAScript发布了新的功能以后,浏览器商需要让自己的浏览器支持这些功能
- 这个过程是需要时间的
- 所以到现在,基本上大部分浏览器都可以比较完善的支持了
- 只不过有些浏览器还是不能全部支持
- 这就出现了兼容性问题
- 所以我们写代码的时候就要考虑哪些方法是ES5或者ES6的,看看是不是浏览器都支持
- let和const关键字
- 我们以前都是使用var关键字来声明变量的
- 在ES6的时候,多了两个关键字let和const.也是用来声明变量的
- 只不过和var有一些区别
- 1et和const不允许重复声明变量
- 1et和const不允许提前使用变量
- const不可以更改值
1.1.ES定义变量
在定义var变量前使用变量并不会报错,因为发生了变量提升
console.log(a) //在定义var变量前使用变量并不会报错,因为发生了变量提升
var a = 100
console.log(a)
- let
与var的区别:- 必须在定义后使用;
- 变量不能重名;
- 块级作用域(只存活于定义它的区域{}):
// console.log(b) //会报错,不能在定义前使用
let b = 100
// let b = 3
console.log(b)
if (1) {
var i = 100
let j = 99 //只存活于定义它的区域
console.log(i)
console.log(j)
}
console.log(i)
// console.log(j) //显示未定义,不能访问
- const
let 变量
const 常量,普通类型其值不能更改,复杂数据类型比如obj,可以更改obj里面的值(但其实也没有更改,因为指向obj的指针并未改变);初始化不赋值也不行
// let 变量
let name = 'xiaoming'
const age = 21
name = 'daming'
// age = 23 不能修改
console.log(name, age)
// const num 初始化不赋值会报错
1.2.案例-块级作用域
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
}
.header {
display: flex;
width: 500px;
}
.header li {
flex: 1;
height: 50px;
line-height: 50px;
text-align: center;
border: 1px solid #000;
}
.box {
position: relative;
}
.box li {
position: absolute;
left: 0;
top: 0;
width: 500px;
height: 200px;
background-color: yellow;
display: none;
}
.header .active {
background-color: #f00;
}
.box .active {
display: block;
}
</style>
</head>
<body>
<ul class="header">
<li class="active">1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<ul class="box">
<li class="active">111</li>
<li>222</li>
<li>333</li>
<li>444</li>
</ul>
<script>
var oHeaderItems = document.querySelectorAll('.header li')
var oBoxItems = document.querySelectorAll('.box li')
/*下面这个方法有坑,可能人为点击来不及响应就已经for循环完了,因此每次点击输出的都是4
for (var i = 0; i < oHeaderItems.length; i++) {
oHeaderItems[i].onclick = function() {
console.log(i)
}
} */
for (let i = 0; i < oHeaderItems.length; i++) {
// 1.自定义属性
oHeaderItems[i].dataset.index = i
// oHeaderItems[i].onclick = handler
oHeaderItems[i].onclick = function() {
console.log(i)
// var 时输出的全是4,因为for循环结束了,我们还没来得及点击;
// let 它有暂存性死区,它会保留当前的值
}
}
</script>
</body>
</html>
1.3.ES6的箭头函数
//普通写法
var test1 = function() {
console.log('11')
}
// 箭头写法
var test2 = () => {
console.log('22')
}
test1()
test2()
箭头写法的特点:
- 1.(只有一个形参时)小括号可以省略;
- 2.{}可以省略 只有一句代码或只有返回值的时候;
- 3.没有arguments;
- 4.箭头函数的this是父级作用域的
//1.(只有一个形参时)小括号可以省略;
var test3 = a => {
console.log('22', a)
}
test3('a')
//2.{}可以省略 只有一句代码或只有返回值的时候
//返回值,这种用法偏多
// 使用这种方式返回对象类型时,记得在{}对象外面加上一个()比如var test4 = a => ({name:'brisa'})
var test4 = a => 100 * a
console.log(test4(2))
var list = ['aaa', 'bbb', 'ccc']
// var newList = list.map(function(item) {
// return `<li>${item}</li>`
// })
var newList = list.map(item => `<li>${item}</li>`)
console.log(newList.join())
// 一句话
var test5 = () => console.log('thanks')
test5()
// 3.没有arguments
var test6 = function() {
console.log(arguments) //函数内自定义的一个特殊变量
console.log(arguments[0], arguments[1], arguments[2])
// console.log(Array.from(arguments))//转成数组
// var test6 = function(a, b, c) {
// console.log(a, b, c)
}
test6(1, 2, 3)
/*var test6 = () => {
console.log(arguments)//报错,箭头函数没有arguments
}
test7(1, 2, 3)*/
// 4.箭头函数的this是父级作用域的
mytext.oninput = function() {
var that = this
setTimeout(function() {
console.log(this) //window
console.log(that) //input 用that赋值,指代父级的this
}, 1000)
setTimeout(() => console.log(this), 1000) //input,父级作用域
}
// 函数的默认参数
function test8(a=1, b=2) {//给1个默认参数,防止程序崩坏
return a + b
}
// var test8 = (a=1,b=2)=>a+b
console.log(test8())
1.4.ES6的解构赋值
ES6的解构赋值:快速的从对象和数组中获取里面的成员
// 快速的从对象和数组中获取里面的成员
var arr = ['xiaoming', 'tiechui', 'shanzhen']
let [x, y, z] = arr //解构赋值
console.log(x) //arr[0]
// 用解构赋值的方式交换值
// 这种交换值方式不能用let定义,会出错
var a = 5
var b = 10
var [a, b] = [b, a] //交换
console.log(a, b)
var arr2 = [1, 2, [3, 4, [5]]] //多维数组
var [i, j, [k, l, [m]]] = arr2 //可以快速获取值
console.log(arr2[2][2][0], m)
var obj = {
name: 'brisa',
age: 100,
location: 'SiChuan',
}
let {
name,age,location: mylocation //冲突时,可以重命名
} = obj
// document.write(obj.name)
// document.write(obj.age)
// document.write(obj.location)
document.write(name)
document.write(age)
document.write(location)//window
document.write(mylocation)
var obj2 = {
name: 'brisa',
age: 100,
location: {
province: 'SiChuan',
city: 'chengdu'
},
hobby: ['1', '2', '3']
}
var {
name,age,
location: {
province,
city
},
hobby: [m, n, l]
} = obj2
console.log(name, age, province, city, m, n, l)
1.5.ES6的对象简写
<input type="text" id="myusername">
<input type="password" id="mypassword">
<button id="mybtn">login</button>
<script>
mybtn.onclick = function() {
let username = myusername.value
let password = mypassword.value
console.log(usn, psw)
/*var obj = {
username: username,
password: password
}*/
// key和value相同(命名<变量名>)时,就可以简写
var obj = {
username,
password
}
console.log('发给后端的结构',
obj)
</script>
var obj1 = {
a: 1,
// getName: function() {
// console.log(this.a)
// }
//简写如下
getName() {
console.log(this.a)
}
}
obj1.getName()
}
1.6.ES6展开运算符
- … 展开数组
var a = [1, 2, 3]
var b = [4, 5, 6]
// console.log(a.concat(b))
//数组
var c = [a, b] //(2) [Array(3), Array(3)]
// 展开
var d = [...a, ...b] //(6) [1, 2, 3, 4, 5, 6]
console.log(c)
console.log(d)
- … 复制
var a = [1, 2, 3]
//b改变会改变a
// var b = a
//b改变不会改变a
// var b = a.slice()
// var b = a.concat()
var b = [...a]
b[0] = 'brisa'
console.log(a, b)
- … 参数-实参-形参
var test1 = function() {
console.log(arguments)
}
/*var test2 = () => {
console.log(arguments) //箭头函数,没有arguments
}*/
//形参的运用
var test3 = (a, b, ...arr) => { //修改为 ...arr就可以正确执行了(接收的是对应参数组成的数组)
// ...arr这种形参必须在最后
console.log(arr)
}
test1(1, 2, 3, 4, 5)
// test2(1, 2, 3)
test3(1, 2, 3, 4, 5)
// 实参的运用
var arr = [1, 2, 35, 5, 6, 78, 9]
var res = Math.max(...arr)
console.log(res)
- … 伪数组转换
function test() {
// arguments会是一个伪数组
// 如何将其转成真正的数组?
// 1. Array.from(arguments)
// 2.
var arr = [...arguments]
console.log(arr)
}
test(1, 2, 3, 4, 5)
var oli = document.querySelectorAll('li')//不是真的数组
var oliarr = [...oli]//转成真的数组
console.log(oli, oliarr)
- …对象
var obj1 = {
name: 'brisa',
age: 100
}
var obj2 = {
name: 'tiechui',
location: 'sichuan'
}
var obj = { //合并对象,同key的会被后面的value覆盖掉
...obj1,
...obj2
}
console.log(obj)
一个关于 …对象的小案例
<h1>修改</h1>
<input type="text" id="myusername">
<input type="number" id="myage">
<button id="btn">修改</button>
<div id="box"></div>
<script>
var obj = {
name: 'brisa',
age: 100,
location: 'sichuan',
id: '20001128',
}
// function render(obj) {
// console.log(obj)
// var {
// name,
// age,
// location
// } = obj
function render({
name,
age,
location
}) { //还可以这样写(解构赋值)
box.innerHTML = `name:${name},age:${age}, location:${location}`
}
render(obj)
btn.onclick = function() {
var name = myusername.value
var age = myage.value
var newObj = { //新的obj,对于原obj没有影响
...obj,
name: name,
age: age
}
//1.传给后端(省略没写)
//2.重新渲染页面
render(newObj)
}
</script>
1.7.ES6模块化语法
模块化:
解决如下问题,1.私密不漏;2.重名不怕;3.依赖不乱
- 私密不漏
//A.js 需要导出
function A1(){//...
}
//...
export {
A1,
A2,
test,
A_A,
}
//html中需要导入
<script type="module">
import {A1, A2, test } from './module/A.js'; A1(); A2(); test();
</script>
- 重名不怕
<script type="module">
// 用 as 重命名
import {A1, A2, test as A_test} from './module/A.js';
import {B1, B2, test as B_test} from './module/B.js';
A_test();
B_test();
</script>
- 依赖不乱
// js需要用到其他js文件中的函数,也可以直接通过export+import使用
import { A_A } from '../module/A.js'
import { B_B } from '../module/B.js'
A_A()
B_B()
function C() {
console.log('C')
}
<!-- 一般放在<head>里 -->
<script src="./module/C.js" type="module"></script>
其他:
如果只用导出一个时,可以使用默认导出:export default XX
,使用import XXX from '地址'
,XX
和XXX
名字不要求一样,因为这里只导出了一个,取不同名字相当于重命名了
2.面向对象
- 首先,我们要明确,面向对象不是语法,是一个思想,是一种编程模式
- 面向:面(脸),向(朝着)
- 面向过程:脸朝着过程=》关注着过程的编程模式
- 面向对象:脸朝着对象=》关注着对象的编程模式
- 实现一个效果
- 在面向过程的时候,我们要关注每一个元素,每一个元素之间的关系,顺序,…
- 在面向过程的时候,我们要关注的就是找到一一个对象来帮我做这个事情,我等待结果
- 我们以前的编程思想是,每一一个功能,都按照需求一步一步的逐步完成
2.1.创建对象的方式
创建对象的方式
因为面向对象就是一个找到对象的过程,所以我们先要了解如何创建一个对象
- 用字面量的方式创建对象
//字面量
var obj1 = {
name: '蒸羊羔',
material: ['111', '222', '333'],
setCook() {
console.log('如何蒸羊羔')
}
}
- 调用系统内置的构造函数创建对象
js 给我们内置了一个Object构造函数,这个构造函数就是用来创造对象的
//内置构造函数
var obj2 = new Object()
obj2 = {
name: '水煮蛋',
material: ['111', '222', '333'],
setCook() {
console.log('如何水煮蛋')
}
}
- 用工厂函数的方式创建对象
//工厂函数
function createObj(name, material) {
var obj = {}
obj.name = name
obj.material = []
return obj
}
var obj3 = createObj('西红柿')
console.log(obj3)
var obj4 = createObj('番茄炒蛋')
console.log(obj4)
- 自定义构造函数创建对象
// 自定义构造函数
// new Object() new String new Array()
function CreateObj2(name) {
//自动创建对象
this.name = name
this.material = []
this.cook = function(){
}
//自动返回对象
}
var obj5 = new CreateObj2('可乐鸡翅')
console.log(obj5)
2.2.构造函数要注意的问题
- 首字母大写
// 1.首字母大写
function CreateObj(name) {
this.name = name
}
var obj1 = new CreateObj('brisa')
console.log(obj1)
-
构造函数不写return
-
构造函数能当成普通函数用?
可以,但没必要
// 3.构造函数能当成普通函数用?
// 可以,但没必要
function CreateObj2(name) {
this.name = name
}
var obj2 = CreateObj2('brisa')
console.log(obj2, window.name) //变成给窗口命名了
- this指向(new 完对象之后所创建的那个对象)
// 4.this指向(new 完对象之后所创建的那个对象)
function CreateObj3(name) {
console.log(this) // new 完对象之后所创建的那个对象
this.name = name
}
var obj3 = new CreateObj3('brisa')//new的过程也叫实例化的过程
console.log(obj3)
2.3.面向对象的原型
function CreateList(select, data) {
this.ele = document.querySelector(select)
this.title = data.title
this.list = data.list
this.render = function() {
// 渲染页面
var h1 = this.ele.querySelector('h1')
var ul = this.ele.querySelector('ul')
h1.innerHTML = this.title
ul.innerHTML = this.list.map(item => `<li>${item}</li>`).join('')
}
}
两个object
会创建相同的render函数
,占用更多的内存空间,造成浪费
<div class="box1">
<h1></h1>
<ul></ul>
</div>
<div class="box2">
<h1></h1>
<ul></ul>
</div>
<script>
var data = {
title: '体育',
list: ['体育-1', '体育-2', '体育-3'],
}
var data2 = {
title: '生物',
list: ['生物-1', '生物-2', '生物-3'],
}
function CreateList(select, data) {
this.ele = document.querySelector(select)
this.title = data.title
this.list = data.list
// 直接在构造对象里面写函数,会浪费空间
// this.render =
}
//原型(共享内存,节省空间)
CreateList.prototype.render = function() {
// 渲染页面
var h1 = this.ele.querySelector('h1')
var ul = this.ele.querySelector('ul')
h1.innerHTML = this.title
ul.innerHTML = this.list.map(item => `<li>${item}</li>`).join('')
}
var obj1 = new CreateList('.box1', data)
// 一模一样
console.log(obj1.__proto__)
console.log(CreateList.prototype)
//为什么这里可以访问到?
// 答:obj会首先看自己有没有render,当发现没有时,会顺着__proto__去找有没有render(原因在于对象.__proto__ === 构造函数.prototype)
obj1.render()
console.log(obj1)
var obj2 = new CreateList('.box2', data2)
obj2.render()
console.log(obj2)
// 对象.__proto__ === 构造函数.prototype
// 扩展: obj1.toString() 为什么可以找到?
// 答:通过原型链
</script>
为什么obj1.toString()也存在?:
- 因为原型链,它会先看自己的构造函数有没有,没有就顺着
__proto__
,看他的构造函数.prototype
,以此沿着原型链继续寻找,最后找到Object.toString()
,然后成功发现存在 Object.prototype.__proto__ -> null
,原型链顶点就是Object.prototype
2.4.案例-选项卡-面向对象
obj.prototype.函数
当创建了对象后,对该对象重写该函数,也不会干扰其他对象相应的函数,因为(原型链),重写后相当于obj自身包含了getName,不用顺着原型链去到prototype上去找相应函数
// ...
CreateObj.prototype.getName = function(){console.log("11111",this.name)}
var obj1 = new Createobj("kerwin")
// 重写obj1的getName函数不会干扰obj2的getName函数
obj1.getName = function(){console.log('2222222',this.name)}
obj1.getName()
// 重写prototype上的函数就会对所有相关obj都有影响
// CreateObj.prototype.getName = function(){console.log("222",this.name)}
var obj2 = new Createobj("tiechui")
obj2.getName()
案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
}
.header {
display: flex;
width: 500px;
}
.header li {
flex: 1;
height: 50px;
line-height: 50px;
text-align: center;
border: 1px solid #000;
}
.box {
position: relative;
height: 200px;
}
.box li {
position: absolute;
left: 0;
top: 0;
width: 500px;
height: 200px;
background-color: yellow;
display: none;
}
.header .active {
background-color: #f00;
}
.box .active {
display: block;
}
</style>
</head>
<body>
<div class="container1">
<ul class="header">
<li class="active">1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<ul class="box">
<li class="active">111</li>
<li>222</li>
<li>333</li>
<li>444</li>
</ul>
</div>
<div class="container2">
<ul class="header">
<li class="active">1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<ul class="box">
<li class="active">111</li>
<li>222</li>
<li>333</li>
<li>444</li>
</ul>
</div>
<!-- <script src="./js/Tabs.js"></script> -->
<script type="module">
import myTabs from './js/Tabs.js'; new myTabs('.container1', 'click'); new myTabs('.container2', 'mouseover')
</script>
</body>
</html>
function Tabs(select, type) {
var container = document.querySelector(select)
this.oHeaderItems = container.querySelectorAll('.header li')
this.oBoxItems = container.querySelectorAll('.box li')
console.log(this.oHeaderItems, this.oBoxItems)
this.type = type//改变触发事件的类型
// 自行调用
this.change()
}
Tabs.prototype.change = function() {
for (let i = 0; i < this.oHeaderItems.length; i++) {
// 1.自定义属性
this.oHeaderItems[i].addEventListener(this.type, () => {
// 2.1获取自身上的属性(使用this,获取当前对象)
var index = i
//2.2所有的都移除active
for (var j = 0; j < this.oHeaderItems.length; j++) {
this.oHeaderItems[j].classList.remove('active')
this.oBoxItems[j].classList.remove('active')
}
//2.3给对应的加上active
this.oHeaderItems[index].classList.add('active')
this.oBoxItems[index].classList.add('active')
})
}
}
export default Tabs
2.5.ES6-class
class CreateObj {
// 构造器函数
constructor(name) {
this.name = name
}
say() {
console.log('hi')
}
}
// CreateObj.prototype.say = function() {
// console.log('hi')
// }
var obj1 = new CreateObj('brisa')
console.log(obj1)
obj1.say()
2.6.面向对象继承
构造函数继承—>属性
原型函数继承—>方法
构造函数继承
function student(name, age, classroom){
Person.call(this, name, age)//继承Person中构造函数里的属性
this.classroom = classroom
}
原型继承
student.prototype = new Person()//继承方法
组合继承!!!
构造函数继承+原型继承
//原有的
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.say = function() {
console.log(this.name, 'hi')
}
重点!!!
//继承的部分
function Student(name, age, grade) {
// 继承属性-方法1
Person.call(this, name, age) //继承属性,强行改变了Person中的this为当前this(当前对象)
// 继承属性-方法2
// Person.apply(this, [name, age])
//自己新增的东西
this.grade = grade
}
// 1.原型函数继承
Student.prototype = new Person()
// 2.增加其他原型函数
Student.prototype.printGrade = function() {
console.log(this.name, this.grade)
}
// 3.覆盖继承来的原型函数
/*Student.prototype.say = function() {
console.log(this.name, 'hello哇')
console.log(this.name, '你好哇')
}*/
// 4.增强-【要改名】
Student.prototype.say2 = function() {
this.say() //首先继承过来
//然后写自己的增强语句
console.log(this.name, 'hello哇')
console.log(this.name, '你好哇')
}
var obj = new Student('brisa', 100, 1000)
console.log(obj)
obj.say2()
// 这种也可以继承
var obj2 = {
grade: 10000
}
Person.call(obj2, 'tiechui', 100)
console.log(obj2)
2.7.继承案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="box1">
<h1></h1>
<img src="" style="width:100px;">
<ul></ul>
</div>
<div class="box2">
<h1></h1>
<img src="" style="width:100px;">
<ul></ul>
</div>
<script>
var data = {
title: '体育',
url: "https://pic.maizuo.com/usr/movie/5011ee407fb407d47e333a3935ec33d1.jpg?x-oss-process=image/quality,070",
list: ['体育-1', '体育-2', '体育-3'],
}
var data2 = {
title: '生物',
url: "https://pic.maizuo.com/usr/movie/5011ee407fb407d47e333a3935ec33d1.jpg?x-oss-process=image/quality,070",
list: ['生物-1', '生物-2', '生物-3'],
}
function CreateList(select, data = {}) {
this.ele = document.querySelector(select)
this.title = data.title
this.list = data.list
}
//原型(共享内存,节省空间)
CreateList.prototype.render = function() {
// 渲染页面
var h1 = this.ele.querySelector('h1')
var ul = this.ele.querySelector('ul')
h1.innerHTML = this.title
ul.innerHTML = this.list.map(item => `<li>${item}</li>`).join('')
}
var obj1 = new CreateList('.box1', data)
// 一模一样
console.log(obj1.__proto__)
console.log(CreateList.prototype)
obj1.render()
console.log(obj1)
/*****************************************************************************/
function CreateImgList(select, data) {
//object继承
CreateList.call(this, select, data)
this.imgUrl = data.url //新增
}
// 原型继承
CreateImgList.prototype = new CreateList()
// 增强
CreateImgList.prototype.enhenceRender = function() {
// 渲染页面
this.render()
var img = this.ele.querySelector('img')
img.src = this.imgUrl
}
var obj2 = new CreateImgList('.box2', data2)
obj2.enhenceRender()
// obj2.render()
console.log(obj2)
</script>
</body>
</html>
2.8.ES6-继承
extends 和 super关键字
// 父类
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
say() {
console.log(this.name, 'hello person')
}
}
//子类 - extends原型继承
class Student extends Person {//extends继承关键字
constructor(name, age, grade) {
super(name, age) //super继承关键字,Person.call(this,name,age)
this.grade = grade
}
//本身就直接继承了方法,但也可以直接重写
say() {
super.say() //重写后,仍然想继承
console.log(this.name, '你好 student')
}
}
var obj = new Student('Brisa', 100, 100)
console.log(obj)
obj.say()
3.前后端交互
3.1.初识前后端交互
前后端大致交互流程
- 需要加载url对应的网页或事件时,网页就会先给它自己的服务器后台发送一个网络请求,这个请求会发送给后台获取所需要的资源信息
- 后端服务器就会从数据库中找来这些资源,并把这些资源信息,比如说文章的地址、标题、封面图片url等等,组装成JSON形式,放在响应体中返回给网页
- 网页获取到响应,便把信息解析并排版成前端人员代码中书写的所要展示的样子
3.2.ajax
3.2.1.ajax初识
ajax
全名async javascript and XML
。是前后台交互的能力,也就是我们客户端给服务端发送消息的工具,以及接受响应的工具,是一个 默认异步 执行机制的功能。
javascript能很好地解析json
AJAX的优势:
1. 不需要插件的支持,原生js 就可以使用
2. 用户体验好(不需要刷新页面就可以更新数据)
3. 减轻服务端和带宽的负担
4. 缺点: 搜索引擎的支持度不够,因为数据都不在页面上,搜索引擎搜索不到
AJAX的使用:
- 在js 中有内置的构造函数来创建 ajax 对象
- 创建 ajax 对象以后,我们就使用 ajax 对象的方法去发送请求和接受响应
ajax 状态码:
- ajax 状态码-
xhr.readystate
- 是用来表示一个 ajax 请求的全部过程中的某一个状态
readystate === 0
: 表示未初始化完成,也就是 open 方法还没有执行readystate === 1
: 表示配置信息已经完成,也就是执行完open 之后readystate === 2
: 表示send 方法已经执行完成readystate === 3
: 表示正在解析响应内容readystate === 4
: 表示响应内容已经解析完毕,可以在客户端使用了
- 这个时候我们就会发现,当一个ajax 请求的全部过程中,只有当 readystate === 4 的时候,我们才可以正常使用服务端给我们的数据
- 所以,配合http 状态码为200~299.
- 一个ajax 对象中有一个成员叫做
xhr.status
- 这个成员就是记录本次请求的 http 状态码的
- 一个ajax 对象中有一个成员叫做
readystatechange:
- 在ajax 对象中有一个事件,叫
readystatechange
事件 - 这个事件是专门用来监听 ajax 对象的
readystate
值改变的的行为。 - 也就是说只要
readystate
的值发生变化了,那么就会触发该事件。 - 所以我们就在这个事件中来监听 ajax的
readystate
是不是到4
了
const xhr = new XMLHttpRequest()
xhr.open('get",./data.php')
xhr.send()
xhr.onreadystateChange = function() {
// 每次 readystate 改变的时候都会触发该事件
// 我们就在这里判断 readystate 的值是不是到 4
// 并且 http 的状态码是不是 200 ~ 299
if (xhr.readystate === 4 && /^2\d{2|$/.test(xhr.status)) {
// 这里表示验证通过
// 我们就可以获取服务端给我们响应的内容了
...
}
}
// 1.创建XHR new XMLHtttpRequest
var xhr = new XMLHttpRequest()
console.log(xhr)
//2.配置 open(请求方式,请求地址,是否异步)
xhr.open('GET', 'http://localhost:5500/31%E5%89%8D%E5%90%8E%E7%AB%AF%E4%BA%A4%E4%BA%92/1.json')
// xhr.open('GET', 'http://localhost:5500/31%E5%89%8D%E5%90%8E%E7%AB%AF%E4%BA%A4%E4%BA%92/2.txt')
//3.send
xhr.send()
//4.接收数据,注册一个事件
//4.1
/*xhr.onreadystatechange = function() {
console.log(xhr.readyState)
if (xhr.readyState === 4 && xhr.status === 200) { //状态码200的话,是正确的状态
console.log('数据解析完成', xhr.responseText)
document.write(xhr.responseText)
} else if (xhr.readyState === 4 && xhr.status === 404) {
console.error('未找到该页面')
location.href = '404.html'
}}*/
//4.2
xhr.onload = function() {
// console.log(xhr.readyState)
// console.log(xhr.responseText) //也会输出
if (xhr.status === 200) {
console.log(JSON.parse(xhr.responseText))
} else if (xhr.status === 404) {
console.error('未找到该页面')
}
}
区别:readystatechange VS load
- 前者状态改变就会执行,调用更为频繁
- 后者只有readyState===4才执行,写法更为简单
3.2.2.ajax案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="btn">获取数据</button>
<ul id="myList"></ul>
<!-- <div class="box"></div> -->
<script>
//http://www.xiongmaoyouxuan.com/api/tabs
btn.onclick = function() {
var xhr = new XMLHttpRequest()
xhr.open('GET', 'http://www.xiongmaoyouxuan.com/api/tabs')
xhr.send()
xhr.onload = function() {
if (xhr.status === 200) {
// console.log(xhr.responseText)
var jsondata = JSON.parse(xhr.responseText)
render(jsondata)
}
}
}
function render(jsondata) {
console.log(jsondata.data.list)
var html = jsondata.data.list.map(item => `<li>
<img src=${item.imageUrl}>
<div>${item.name}</div>
</li>`)
myList.innerHTML = html.join('')
// console.log(html.join(''))
}
</script>
</body>
</html>
3.2.3.ajax同步异步
xhr的open方法的第三个参数有什么用?
第三个参数代表是不是异步(async),默认值为true,也就是使用异步的方式不阻塞主线程。如果值为false,则不使用异步的方式,在主线程知道xhr的请求结束之后才会继续执行后面的语句。
var xhr = new XMLHttpRequest()
//相对路径也可以
// true 异步请求; false 同步请求
xhr.open('GET', './1.json', false) //要么不传第三个参数,要传就传true
//同步执行时,若send放前面,会导致数据传回时回调函数堵塞,不能正常执行回调函数,因此建议这种情况下,将回调函数放send前面
//不过更建议使用异步请求
xhr.onload = function() {
if (xhr.status === 200) {
console.log(xhr.responseText)
}
}
xhr.send()
console.log(11111)
3.2.4.请求方式
请求方式
form:
get
、post
ajax:
get
:明文传输,不够安全(偏向于获取数据)
post
:在body请求体中传递数据,足够安全(偏向于提交数据)
put
: 偏向于更新数据(全部更新)
delete
:偏向删除信息
patch
:(偏向于局部更新)
header
: 获取服务器头部信息
options
: 获取服务器设备信息
connect
: 保留请求方式
使用nodejs中的json-server来模拟后端接口
1.下载安装nodejs
2.集成终端下载(npm install json-server -g)
3.使用 (json-server 路径)
json-server - 基于一个json文件就可以创建很多后端模拟接口
- GET方式获取数据
GET传参 在url后加
?xx=xxx&&xx=xxx
myget.onclick = function() {
var xhr = new XMLHttpRequest()
// xhr.open('GET', 'http://localhost:3000/users')
// GET传参
xhr.open('GET', 'http://localhost:3000/users?username=brisa&&password=123456')
xhr.onload = function() {
if (xhr.status === 200) {
console.log(JSON.parse(xhr.responseText))
}
}
xhr.send()
}
- POST方式传输数据
需要添加传输的数据种类信息(form表单字符串、json字符串),设置请求头
post 提交信息:
1. name=brisa&&age=18 表单字符串xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
2. {“name”:“kerwin”} json字符串xhr.setRequestHeader("Content-Type", "application/json")
mypost.onclick = function() {
var xhr = new XMLHttpRequest()
xhr.open('POST', 'http://localhost:3000/users')
xhr.onload = function() {
if (/^2\d{2}$/.test(xhr.status)) {
console.log(JSON.parse(xhr.responseText))
}
}
// 给指定的HTTP请求头赋值——表单字符串
// xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded") //name=brisa&&age=18
// xhr.send(`username=xixi&&password=123456`)
//给指定的HTTP请求头赋值——json字符串
xhr.setRequestHeader("Content-Type", "application/json") //{"name":"kerwin"}
xhr.send(JSON.stringify({
name: "kerwin",
password: "123456"
}))
}
- PUT
完全覆盖哟
只写name:balala的话,password这个会丢失
需要写清楚要修改的是哪条数据(id)
myput.onclick = function() {
var xhr = new XMLHttpRequest()
xhr.open('PUT', 'http://localhost:3000/users/12')//修改的是id=12的
xhr.onload = function() {
if (xhr.status === 200) {
console.log(JSON.parse(xhr.responseText))
}
}
//设置请求头
xhr.setRequestHeader("Content-Type", "application/json")
xhr.send(JSON.stringify({
name: "balala",
password: "123456"
}))
}
- PTACH
部分修改,只会修改要修改的部分(要id)
mypatch.onclick = function() {
var xhr = new XMLHttpRequest()
xhr.open('PATCH', 'http://localhost:3000/users/13')
xhr.onload = function() {
if (xhr.status === 200) {
console.log(JSON.parse(xhr.responseText))
}
}
//设置请求头
xhr.setRequestHeader("Content-Type", "application/json")
xhr.send(JSON.stringify({
name: "zhouzhou",
// password: "abcdef"
}))
}
- DELETE
也要传ID
mydelete.onclick = function() {
var xhr = new XMLHttpRequest()
xhr.open('DELETE', 'http://localhost:3000/users/13') //给一个ID
xhr.onload = function() {
if (xhr.status === 200) {
console.log(JSON.parse(xhr.responseText))
}
}
xhr.send()
}
3.2.5.ajax封装
util.js
function queryStringify(obj) { //处理非stringdata,将其转化为form表单传输字符串的形式
let str = ''
for (let k in obj) str += `${k}=${obj[k]}&`
return str.slice(0, -1)
}
function ajax(options) {
let defaultoptions = { //设置一些默认参数,以防输入缺失
url: "",
method: "GET",
async: true,
data: {},
headers: {"content-type":"application/x-www-form-urlencoded"},
success: function() {},
error: function() {}
}
let { url, method, async, data, headers, success, error } = { //参数传入
...defaultoptions,
...options
}
//对数据进行处理,如果headers是json类型且data是object类型,就用json处理data的形式
// if ((typeof data === 'object') && (headers['content-type']?.indexOf('json') > -1)) {//?表示不确定是否存在
if (typeof data === 'object' && headers['content-type'] ? headers['content-type'].indexOf('json') > -1 : 0) {
data = JSON.stringify(data)
} else { //否则使用form表单处理数据的形式
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)) { //状态码判断(不是200-299的就报错并结束后续执行)
console.log('error')
error(`错误状态码:${xhr.status}`) //回调(传进来的函数被调用)
return
}
// 执行解析-使用try catch捕获错误
try {
let result = JSON.parse(xhr.responseText)
console.log(result)
success(result)
} 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)
}
}
使用封装的utils.js
<script src="./util.js"></script>
<script>
ajax({
url: "http://localhost:3000/users",
method: "POST",
// data: "username=xila&password=123456",
data: {
username: "xilala",
password: 123456
},
headers: {
// "content-type": "application/x-www-form-urlencoded"
"content-type": "application/json"
},
success: function(res) {
console.log(res, 'success')
},
error: function(err) {
console.log(err, 'error')
}
})
</script>
ajax({
url: "http://localhost:3000/users",
method: "GET",
async: true,
data: {
username: "bill",
password: "123"
},
headers: {},
success: function(res) {
console.log(res)
},
error: function(err) {
console.log(err)
}
})
一个封装小案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="myget">get</button>
<button id="mypost">post</button>
<button id="myput">put</button>
<button id="mypatch">patch</button>
<button id="mydelete">delete</button>
<script src="./util.js"></script>
<script>
myget.onclick = function() {
ajax({
url: "http://localhost:3000/users",
data: {
username: "brisa",
password: "123456"
},
success: function(res) {
console.log(res, 'success')
},
error: function(err) {
console.log(err, 'error')
}
})
}
mypost.onclick = function() {
ajax({
url: "http://localhost:3000/users",
method: "POST",
data: {
username: "ximi",
password: "123456"
},
headers: {
"content-type": "application/json"
},
success: function(res) {
console.log(res, 'success')
},
error: function(err) {
console.log(err, 'error')
}
})
}
myput.onclick = function() {
ajax({
url: "http://localhost:3000/users/12",
method: "PUT",
data: {
username: "wahaha",
password: "abcded"
},
headers: {
"content-type": "application/json"
},
success: function(res) {
console.log(res, 'success')
},
error: function(err) {
console.log(err, 'error')
}
})
}
mypatch.onclick = function() {
ajax({
url: "http://localhost:3000/users/10",
method: "PATCH",
data: {
username: "ximila",
// password: "123456"
},
headers: {
"content-type": "application/json"
},
success: function(res) {
console.log(res, 'success')
},
error: function(err) {
console.log(err, 'error')
}
})
}
mydelete.onclick = function() {
ajax({
url: "http://localhost:3000/users/9",
method: "DELETE",
// data: {
// username: "ximila",
// // password: "123456"
// },
// headers: {
// "content-type": "application/json"
// },
success: function(res) {
console.log(res, 'success')
},
error: function(err) {
console.log(err, 'error')
}
})
}
</script>
</body>
</html>
3.2.6.前后端交互案例
总结:
1.考虑开发思维,这里选择的是面向对象——面向对象,那么就把list当做一个对象,对其的各种操作【初始化,维护】【增删查改】也封装在对象里面
2.事件绑定-用的事件委托
3.页面渲染-ajax
4.删除时,使用自定义属性,便于获取id
5.删除时,无所谓页面和数据库的删除顺序。但是,添加时,应该先加入数据库,再重新对页面进行渲染!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<input type="text" name="" id="mytext" />
<button id="myadd">add</button>
<ul class="list"></ul>
<script type="module">
import ajax from "./util.js";
// 把整个列表封装为一个对象,对其的增删查改直接调用内部的方法
class TodoList {
constructor(select) {
this.listEle = document.querySelector(select);
this.listdata = []//列表数据
this.init()
}
//初始化
init(){
//初始化
this.bindEvent()
//获取数据的方法
this.getList()
}
bindEvent(){
this.listEle.onclick = (evt)=>{
if(evt.target.nodeName==="BUTTON"){
this.removeItem(evt.target)
}
}
}
getList(){
//获取数据
ajax({
url:"http://localhost:3000/list",
success:(res)=>{
this.listdata = res
this.render()
},
error:function(res){
}
})
}
//渲染页面
render(){
this.listEle.innerHTML= this.listdata.map(item=>`
<li>
${item.text}
<button data-index=${item.id}>del</button>
</li>
`).join("")//增加一个自定义属性,方便后续获取id
}
addItem(text){
//在数据库中添加后,成功完成回调后,再渲染页面
ajax({
url:`http://localhost:3000/list`,
method:"POST",
data:{text:text},//同名,可以直接简写为text
success:(res)=>{
// console.log('成功添加',res)
// location.reload()//全局刷新页面,不合理
this.listdata = [...this.listdata, res]//追加
this.render()
},
error:function(res){
}
})
}
removeItem(target){
//删除如何处理?可以运用事件委托,绑定到ul上,冒泡冒到该项来进行删除
//节点删除
target.parentNode.remove()
// console.log(target.dataset.index)
//数据库删除
ajax({
url:`http://localhost:3000/list/${target.dataset.index}`,
method:"DELETE",
success:(res)=>{
console.log('成功删除')
},
error:function(res){
}
})
}
updateItem(){
}
}
var obj = new TodoList(".list")
// console.log(obj)
myadd.onclick = ()=>{
// console.log(mytext.value)
obj.addItem(mytext.value)
}
</script>
</body>
</html>
3.3.Promise
- promise是一个ES6的语法
- 承诺的意思
- 是一个专门用于解决异步回调地狱的问题的方法
3.3.1.回调地狱问题
回调地狱/嵌套金字塔
- 当一个回调函数嵌套一个回调函数的时候就会出现一个嵌套结构
- 当嵌套的多了就会出现回调地狱的情况
- 比如我们发送三个 ajax 请求
- 第一个正常发送
- 第二个请求需要第一个请求的结果中的某一个值作为参数
- 第三个请求需要第二个请求的结果中的某一个值作为参数
- 会导致代码混乱、不好维护等问题
//ajax里嵌入ajax,继续套娃
//tiechui已经登录
ajax({
url:"http://localhost:3000/news",
data:{
author:"tiechui"
},
success:function(res){
//获取对应ID
console.log(res[0].id)
//获取相应评论
ajax({
url:"http://localhost:3000/comments",
data:{
newsId:res[0].id
},
success:function(res){
console.log(res)
}
})
}
})
3.3.2.Promise的基础语法
语法:
new Promise(function(resolve, reject){
// resolve 表示成功的回调
// reject 表示失败的回调
}).then(function(res){
// 成功的函数
}).catch(function(err) {
// 失败的函数
})
- promise 就是一个语法
- 我们的每一个异步事件,在执行的时候。都会有三个状态,
执行中/成功/失败
——pending 执行中;fulfilled 成功;reject 失败
- 我们的每一个异步事件,在执行的时候。都会有三个状态,
- 因为它包含了成功的回调函数
- 所以我们就可以使用 promise 来解决多个 ajax 发送的问题
//promise基础语法
// Promise 构造函数
var q = new Promise(function(resolve,reject){
//异步代码
setTimeout(()=>{
//成功兑现承诺
resolve(["111","222","333"])
//失败拒绝承诺
// reject("error")
},2000)
})
// 3个状态
// pending 执行中
// fulfilled 成功
// reject 失败
//q是promise对象
q.then(function(res){
//兑现承诺,这个函数被执行
console.log("success",res)
}).catch(function(err){
//拒绝承诺,这个函数被执行
console.log("fail",err)
})
3.3.3.Promise封装ajax
//util.js中加入以下代码
function pajax(options){
return new Promise((resolve,reject)=>{
ajax({
...options,
success(res){
resolve(res)
},
error(err){
reject(err)
}
})
})
}
<script src="./util.js"></script>
<script>
//tiechui已经登录
pajax({
url: "http://localhost:3000/news",
data: {
author: "tiechui",
},
})
.then((res) => {
// console.log(res)
return pajax({
url: "http://localhost:3000/comments",
data: {
newsId: res[0].id,
},
}).then((res) => {
console.log("success",res);
});
})
.catch((err) => {
console.log(err);
});
</script>
3.3.4.Promise版ajax案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<input type="text" name="" id="mytext" />
<button id="myadd">add</button>
<ul class="list"></ul>
<script type="module">
import {pajax} from "./util.js";
// 把整个列表封装为一个对象,对其的增删查改直接调用内部的方法
class TodoList {
constructor(select) {
this.listEle = document.querySelector(select);
this.listdata = []//列表数据
this.init()
}
//初始化
init(){
//初始化
this.bindEvent()
//获取数据的方法
/*this.getList()*/
this.getList().then((res)=>{
this.listdata = res
this.render()
}).catch(err=>{})
}
bindEvent(){
this.listEle.onclick = (evt)=>{
if(evt.target.nodeName==="BUTTON"){
this.removeItem(evt.target)
}
}
}
getList(){
//获取数据
/*pajax({
url:"http://localhost:3000/list"
}).then((res)=>{
this.listdata = res
this.render()
}).catch(err=>{})}*/
return pajax({
url:"http://localhost:3000/list"
})
}
//渲染页面
render(){
this.listEle.innerHTML= this.listdata.map(item=>`
<li>
${item.text}
<button data-index=${item.id}>del</button>
</li>
`).join("")//增加一个自定义属性,方便后续获取id
}
addItem(text){
//在数据库中添加后,成功完成回调后,再渲染页面
pajax({
url:`http://localhost:3000/list`,
method:"POST",
data:{text:text},//同名,可以直接简写为text
}).then((res)=>{
// console.log('成功添加',res)
// location.reload()//全局刷新页面,不合理
this.listdata = [...this.listdata, res]//追加
this.render()
}).catch(err=>{
})
}
removeItem(target){
//删除如何处理?可以运用事件委托,绑定到ul上,冒泡冒到该项来进行删除
//节点删除
target.parentNode.remove()
// console.log(target.dataset.index)
//数据库删除
pajax({
url:`http://localhost:3000/list/${target.dataset.index}`,
method:"DELETE",
}).then((res)=>{
console.log('成功删除')
}).catch(err=>{})
}
updateItem(){
}
}
var obj = new TodoList(".list")
// console.log(obj)
myadd.onclick = ()=>{
// console.log(mytext.value)
obj.addItem(mytext.value)
}
console.log("正在加载中")
/*要求:显示looplist和datalist
问题:当数据太多未能马上加载时,要显示"loading",数据加载完后显示数据,但是得要所有数据都加载完成后才显示数据,如何实现?
方法:
1.写出两个pajax及对应的then。这种方法会导致先加载完的数据先显示,不方便实现隐藏加载效果
2.利用链式then,可以但没必要,浪费时间
3.用Promise.all()
*/
/*pajax({
url:"http://localhost:3000/loopList"
}).then(res=>{
console.log(res)
console.log('隐藏加载中')
})
pajax({
url:"http://localhost:3000/dataList"
}).then(res=>{
console.log(res)
})*/
var q1 = pajax({
url:"http://localhost:3000/loopList"
})
var q2 = pajax({
url:"http://localhost:3000/dataList"
})
Promise.all([q1,q2]).then(res=>{
console.log(res)
console.log("隐藏加载")
}).catch(err=>{console.log(err)})
</script>
</body>
</html>
3.4.async/await
3.4.1.async/await语法
async/await
是一个es7
的语法- 这个语法是回调地狱的终极解决方案
- 语法:
async function fn()
{
const res = await promise对象
}
- 这个是一个特殊的函数方式
- 可以await一个promise 对象
- 可以把异步代码写的看起来像同步代码
- 只要是一个promise 对象,那么我们就可以使用
async/await
来书写
async function fn() {
const res = new Promise(function(resolve, reject{
ajax({
ur1:第一个地址,
success (res) {
reso1ve(res)
}
})
}
}
<script type="module">
import { pajax } from "./util.js";
async function test() {
//只针对内部
//怎么在async里实现捕获错误-try+catch
try {
// await 同步代码(没有意义)/promise对象
var res = await pajax({
url: "http://localhost:3000/news",
data: {
author: "tiechui",
},
});
console.log("async+await", res); //用同步实现了异步(实现顺序执行了)
// console.log(222)
//链式执行
//之前是一直then,现在可以直接这种写法
var res1 = await pajax({
url: "http://localhost:3000/comments",
data: {
newsId: res[0].id,
},
});
return res1;
} catch (err) {
console.log("err", err);
}
}
/*想在外面使用res只能通过then实现,否则会因为pending及异步而不能正常返回值*/
test()
.then((res) => {
console.log("then", res);
})
// .catch((err) => {
// console.log(err);
// });
console.log(3333);
</script>
3.4.2.async/await案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<input type="text" name="" id="mytext" />
<button id="myadd">add</button>
<ul class="list"></ul>
<script type="module">
import {pajax} from "./util.js";
// 把整个列表封装为一个对象,对其的增删查改直接调用内部的方法
class TodoList {
constructor(select) {
this.listEle = document.querySelector(select);
this.listdata = []//列表数据
this.init()
}
//初始化
init(){
//初始化
this.bindEvent()
//获取数据的方法
/*this.getList()*/
this.getList().then((res)=>{
this.listdata = res
this.render()
}).catch(err=>{})
}
bindEvent(){
this.listEle.onclick = (evt)=>{
if(evt.target.nodeName==="BUTTON"){
this.removeItem(evt.target)
}
}
}
async getList(){
//获取数据
var res= await pajax({
url:"http://localhost:3000/list"
})
// console.log("getList",res)
return res
}
//渲染页面
render(){
this.listEle.innerHTML= this.listdata.map(item=>`
<li>
${item.text}
<button data-index=${item.id}>del</button>
</li>
`).join("")//增加一个自定义属性,方便后续获取id
}
async addItem(text){
//在数据库中添加后,成功完成回调后,再渲染页面
var res = await pajax({
url:`http://localhost:3000/list`,
method:"POST",
data:{text:text},//同名,可以直接简写为text
})
// .then((res)=>{
// // console.log('成功添加',res)
// // location.reload()//全局刷新页面,不合理
// this.listdata = [...this.listdata, res]//追加
this.listdata = [...this.listdata, res]//追
this.render()
// }).catch(err=>{
// })
}
async removeItem(target){
target.parentNode.remove()
await pajax({
url:`http://localhost:3000/list/${target.dataset.index}`,
method:"DELETE",
})
console.log('成功删除')
// .then((res)=>{
// console.log('成功删除')
// }).catch(err=>{})
}
updateItem(){
}
}
var obj = new TodoList(".list")
myadd.onclick = ()=>{
obj.addItem(mytext.value)
}
console.log("正在加载中")
var q1 = pajax({
url:"http://localhost:3000/loopList"
})
var q2 = pajax({
url:"http://localhost:3000/dataList"
})
// Promise.all([q1,q2]).then(res=>{
// console.log(res)
// console.log("隐藏加载")
// }).catch(err=>{console.log(err)})
async function test(){
var res = await Promise.all([q1,q2])
console.log(res)
console.log("隐藏加载")
}
test()
</script>
</body>
</html>
3.5.fetch
XMLHttpRequest
是一个设计粗糙的API,配置和调用方式非常混乱,而且基于事件的异步模型写起来不友好。兼容性不好。 polyfill: https://github.com/camsong/fetch-ie8
3.5.1.fetch基础语法
fetch适用于取代XMLHttpRequest的,能够很好地解决异步和链式
- fetch默认get请求
- 中间部分可以简写 then(res=>res.json())
- res.json() 数组形式的json ;res.text()字符串
- get方式传参直接暴力拼接在后面
- 要先判断,错误发生不像promise中可以直接用catch
- headers和body的格式
headers: {
"content-type": "application/x-www-form-urlencoded"
},
body: "username=ximi&password=123456"
}
headers: {
"content-type": "application/json"
},
body: JSON.stringify({
username: "ximi",
password: "123456"
})
- method大小写都行
get
myget.onclick = function() {
//fetch默认get请求
fetch("http://localhost:3000/users").then(res=>{
return res.json()//json格式的promise对象
}).then(res=>{
console.log(res)//第二个then才能拿到最终数据
})
//简写
//res.json() 数组形式的json ;res.text()字符串
fetch("http://localhost:3000/users").then(res=>res.json()).then(res=>{
console.log(res)
})
// get传参直接暴力拼接在后面
var username = 'xiaoming'
fetch(`http://localhost:3000/users?username=${username}`).then(res=>res.json()).then(res=>{
console.log(res)
})
//错误捕获
fetch("http://localhost:3000/users111").then(res=>{
console.log(res)
if(res.ok){
return res.json()
}else{
//拒绝,使用promise自带的方法
return Promise.reject({
status:res.status,
statusText:res.statusText
})
}
}).then(res=>{
console.log("success",res)
}).catch(err=>{
console.log("error",err)
})
}
POST
mypost.onclick = function() {
fetch("http://localhost:3000/users",{
method: "POST",
/*headers: {
"content-type": "application/x-www-form-urlencoded"
},
body: "username=ximi&password=123456"
}*/
headers: {
"content-type": "application/json"
},
body: JSON.stringify({
username: "ximi",
password: "123456"
})
}).then(res=>{
console.log(res)
if(res.ok){
return res.json()
}else{
return Promise.reject({
status:res.status,
statusText:res.statusText
})
}
}).then(res=>{
console.log("success",res)
}).catch(err=>{
console.log("error",err)
})
}
PUT
myput.onclick = function() {
fetch("http://localhost:3000/users/2",{
method: "PUT",
headers: {
"content-type": "application/json"
},
body: JSON.stringify({
username: "ALILALA",
password: "123456"
})
}).then(res=>{
console.log(res)
if(res.ok){
return res.json()
}else{
return Promise.reject({
status:res.status,
statusText:res.statusText
})
}
}).then(res=>{
console.log("success",res)
}).catch(err=>{
console.log("error",err)
})
}
PATCH
mypatch.onclick = function() {
fetch("http://localhost:3000/users/3",{
method: "PATCH",
headers: {
"content-type": "application/json"
},
body: JSON.stringify({
username: "ALIA",
// password: "123456"
})
}).then(res=>{
console.log(res)
if(res.ok){
return res.json()
}else{
return Promise.reject({
status:res.status,
statusText:res.statusText
})
}
}).then(res=>{
console.log("success",res)
}).catch(err=>{
console.log("error",err)
})
}
DELETE
mydelete.onclick = function() {
fetch("http://localhost:3000/users/13",{
method: "DELETE",
}).then(res=>{
console.log(res)
if(res.ok){
return res.json()
}else{
return Promise.reject({
status:res.status,
statusText:res.statusText
})
}
}).then(res=>{
console.log("success",res)
}).catch(err=>{
console.log("error",err)
})
}
3.5.2.fetch案例
案例一
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" name="" id="mytext">
<button id="myadd">add</button>
<ul class="list"</u1>
<script type="module">
async function test(){
var author = "tiechui"
var res = await fetch(`http://localhost:3000/news?author=${author}`).then(res=>res.json())
console.log(res)
var res1 = await fetch(`http://localhost:3000/news?comments=${res[0].id}`).then(res=>res.json())
console.log(res1)
}
test()
</script>
</body>
</html>
案例二
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<input type="text" name="" id="mytext" />
<button id="myadd">add</button>
<ul class="list"></ul>
<script type="module">
// 把整个列表封装为一个对象,对其的增删查改直接调用内部的方法
class TodoList {
constructor(select) {
this.listEle = document.querySelector(select);
this.listdata = []//列表数据
this.init()
}
//初始化
init(){
//初始化
this.bindEvent()
//获取数据的方法
/*this.getList()*/
this.getList().then((res)=>{
this.listdata = res
this.render()
}).catch(err=>{})
}
bindEvent(){
this.listEle.onclick = (evt)=>{
if(evt.target.nodeName==="BUTTON"){
this.removeItem(evt.target)
}
}
}
async getList(){
//获取数据
var res= await fetch("http://localhost:3000/list").then(res=>res.json())
return res
}
//渲染页面
render(){
this.listEle.innerHTML= this.listdata.map(item=>`
<li>
${item.text}
<button data-index=${item.id}>del</button>
</li>
`).join("")//增加一个自定义属性,方便后续获取id
}
async addItem(text){
//在数据库中添加后,成功完成回调后,再渲染页面
var res = await fetch(`http://localhost:3000/list`,{
method:"POST",
headers:{"content-type":"application/json"},
body:JSON.stringify({text}),//同名,可以直接简写为text
}).then(res=>res.json())
this.listdata = [...this.listdata, res]//追
this.render()
}
async removeItem(target){
target.parentNode.remove()
await fetch(`http://localhost:3000/list/${target.dataset.index}`,{
method:"DELETE",
}).then(res=>res.json())
console.log('成功删除')
}
updateItem(){}
}
var obj = new TodoList(".list")
myadd.onclick = ()=>{
obj.addItem(mytext.value)
}
console.log("正在加载中")
var q1 = fetch("http://localhost:3000/loopList").then(res=>res.json())
var q2 = fetch("http://localhost:3000/dataList").then(res=>res.json())
async function test(){
var res = await Promise.all([q1,q2])
console.log(res)
console.log("隐藏加载")
}
test()
</script>
</body>
</html>
3.6.大案例
list.js
//面向过程
//1.获取数据
var current = 0//记录当前是第几页
var isLoading = false//记录是否正在请求(防止多次请求)
var total = 0//记录总数据长度
getList()
async function getList(){
current++
//这种获取方法也可以
var res = await fetch(`http://localhost:3000/goods?_page=${current}&_limit=5`)
//获取响应头的数据
total = res.headers.get('X-Total-Count')//获取到的是字符串
var list = await res.json()
// console.log(list)
renderHTML(list)
return list
}
//2.数据加载,将内容返回到页面
function renderHTML(arr) {
for (let i = 0; i < arr.length; i++) {
var oli = document.createElement('li')
oli.innerHTML = `<li>
<img src="http://localhost:3000${arr[i].poster}" alt="">
<h3>${arr[i].title}</h3>
</li>`
//图片路径访问问题,通过json-server可以在3000接口后面直接访问db下的public下面的图片,并且可以省略public
//事件绑定
oli.onclick = function(){
//跳转
location.href = `detail.html?id=${arr[i].id}`
}
list.appendChild(oli) //append不会导致屏幕一闪一闪的
}
}
//3.窗口滚动时进行判断
window.onscroll = function() {
//最后一条数据拿到了,就不要再滚动了
//怎么判断-通过响应头里的X-Total-Count
// console.log(list.children.length,total)
if(list.children.length===Number(total)) return
var listHeight = list.offsetHeight
var listTop = list.offsetTop
var scrollTop = document.documentElement.scrollTop ||
document.body.scrollTop
var windowHeight = document.documentElement.clientHeight
if (isLoading) return
if ((listHeight + listTop) - (Math.round(windowHeight + scrollTop)) < 100) {
console.log('到底')
isLoading = true
//渲染下一页数据
getList().then(()=>{
isLoading=false//下一次触发
})
}
}
list.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./css/list.css"></head>
<body>
<h1>标题</h1>
<ul id="list">
</ul>
<script src="./js/list.js"> </script>
</body>
</html>
detail.js
//拿列表传来的ID
// 1.截取字符串
// console.log(location.href.split('=')[1])
// 2.URL构造函数
var obj = new URL(location.href)
// console.log(obj)
// console.log(obj.searchParams.get('id'))
var id = obj.searchParams.get('id')
//面向对象
class Detail {
constructor(id){
this.id = id
this.init()
}
async init(){
//获取数据
var info = await this.getList()
//渲染页面
this.renderHTML(info)
}
async getList(){
var res = await fetch(`http://localhost:3000/goods/${this.id}`)
var info = await res.json()
return info
}
async renderHTML(info){
console.log(info)
var {title, feature, price,desc} = info
var oh1 = document.querySelector('h1')
var ofeature = document.querySelector('.feature')
var oprice = document.querySelector('.price')
var olist = document.querySelector('.list')
oh1.innerHTML = title
ofeature.innerHTML = feature
oprice.innerHTML = `<span style="color:red;">价格:¥${price}</span>`
olist.innerHTML = desc.map(item=>
`<li><img src="${item}"/></li>`
).join("")
}
}
new Detail(id)
detail.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./css/detail.css">
</head>
<body>
<h1>详情页面</h1>
<div class="feature"></div>
<div class="price"></div>
<ul class="list"></ul>
<script src="./js/detail.js"></script>
</body>
</html>
3.7.cookie
cookie的特点:
- 只能存储文本
- 单条存储有大小限制4KB左右
数量限制(一般浏览器,限制大概在50条左右)- 读取有域名限制:不可跨域读取,只能由来自 写入cookie的 同一域名 的网页可进行读取。简单的讲就是,哪个服务器发给你的cookie,只有哪个服务器有权利读取
- 时效限制:每个cookie都有时效,默认的有效期是,会话级别:就是当浏览器关闭,那么cookie立即销毁,但是我们也可以在存储的时候手动设置cookie的过期时间
- 路径限制:存cookie时候可以指定路径,只允许子路径读取外层cookie,外层不能读取内层。
//怎么判断是否登录?
//http协议是无状态的协议
//localStorage--BOM中讲过,不便直接存密码
//cookie-本地存储 sessionID 钥匙===>Node.js cookie+session
//cookie 本地存储技术
//存cookie
savebtn.onclick = function(){
//只能一条一条存
//重名的话,后面覆盖前面的
document.cookie = "age=18"
//路径设置——path
// document.cookie = "username=brisa;path=/155-cookie/aaa"
//过期时间设置——expires
var date = new Date()
date.setMinutes(date.getMinutes()+1)
document.cookie = `username=brisa;expires=${date.toUTCString()}`
}
getbtn.onclick = function(){
//HttpOnly属性的只能由后端读取,前端直接访问不到
console.log(getCookie('age'))
console.log(getCookie('username'))
}
function getCookie(key){//获取cookie中相应key的value
var str = document.cookie
var arr = str.split(';')
// console.log(arr)
var obj = {}
for(var i=0;i<arr.length;i++){
var subArr = arr[i].split('=')
// console.log(sub)
obj[subArr[0]]=subArr[1]
}
// console.log(obj)
return obj[key]
}
//删
delbtn.onclick = function(){
//把expires设置为当前时间往前推
var date = new Date()
date.setMinutes(date.getMinutes()-1)
document.cookie = `username=brisa;expires=${date.toUTCString()}`
document.cookie = `age=18;expires=${date.toUTCString()}`
}
3.8.jsonp
3.8.1.jsonp基本原理
Jsonp(JSON with Padding)
是 json 的一种"使用模式",可以让网页从别的域名(网站)那获取资料,即跨域读取数据。
为什么我们从不同的域(网站)访问数据需要一个特殊的技术( JSONP )呢?这是因为同源策略。
同源策略:同域名、同端口号、同协议
不符合同源策略,浏览器为了安全,会阻止请求
解决跨域问题
- cors 由服务端或后端设置,Access-Control-Allow-Origin
- jsonp 前后端协作完成
json原理:动态创建script标签,src属性指向没有跨域限制
指向一个接口,接口返回的格式一定是 ****() 函数表达式
注意:
1.后端接口形式必须是 **(),需要后端配合
2.jsonp的缺点:(1)onload删除节点(2)只能get请求,不能post put patch delete
// function test(obj){
// console.log(obj)
// }
function callbackFunction(obj){
console.log(obj)
}
mybtn.onclick = function(){
// var oscript = document.createElement('script')
// oscript.src= '01.txt'//未来地址
// document.body.appendChild(oscript)
var oscript = document.createElement('script')
oscript.src= "https://www.runoob.com/try/ajax/jsonp.php?jsoncallback=callbackFunction"//未来地址
document.body.appendChild(oscript)
//访问完成后就可以删掉该节点
oscript.onload = function(){
//删除当前节点
oscript.remove()
}
}
3.8.2.jsonp案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" id="mysearch">
<ul id="list"></ul>
<script>
mysearch.oninput = function(evt){
if(!evt.target.value){
list.innerHTML = ''
return}
// console.log(evt.target.value)
var oscript = document.createElement('script')
oscript.src = `https://www.baidu.com/sugrec?pre=1&p=3&ie=utf-8&json=1&prod=pc&from=pc_web&sugsid=36547,37558,37520,38093,38126,38116,37989,37925,26350,38099,38008,37881&wd=${evt.target.value}&req=2&csor=1&cb=brisa&_=1675842510792`
document.body.appendChild(oscript)
oscript.onload = function(){
oscript.remove()
}
}
function brisa(obj){
list.innerHTML = obj.g.map(item=>`<li>${item.q}</li>`).join('')
}
</script>
</body>
</html>
3.9.再谈函数
函数定义阶段会在栈区和堆区占用空间,函数调用阶段,会临时申请一片区域用于使用,调用完成后空间就会被销毁
如何实现执行空间不被销毁?
函数有返回值,而且返回值必须是复杂类型,而且要赋值给外面的变量
想收回时,直接将外面的那个变量赋值为null
//函数有返回值,而且返回值必须是复杂类型,而且要赋值给外面的变量
function test(){
var name='brisa'//外面调用时,也仍然被直接销毁
console.log(name)
var obj = {
a:1,
b:2
}
return obj
}
var obj = test()
console.log(obj)
obj= null//收回空间
3.10.闭包
3.10.1.对闭包的理解
闭包:函数内部返回一一个函数(或对象中包含着一个函数),被外界引用。这个内部函数就不会被销毁。内部函数所用到的外部函数的变量也不会被销毁。
优点:让临时变量永驻内存
缺点:内存泄漏 func=null回收
<script>
function outer(){
var name='brisa'
return function(){
return name+'111'//name不会被销毁
}
}
var func = outer()
console.log(func())
</script>
闭包-函数柯里化
<script>
//减少变量传输
// fetch('http://www.a.com/aaa')
// fetch('http://www.a.com/bbb')
// fetch('http://www.b.com/bbb')
// fetch('http://www.b.com/bbb')
function FetchContainer(url){
return function(path){
return fetch(url+path)
}
var fetcha = FetchContainer('http://www.a.com')
fetcha('./aaa').then(res=>res.json()).then(res=>console.log(res))
fetcha('./bbb').then(res=>res.json()).then(res=>console.log(res))
fetcha = null
var fetchb = FetchContainer('http://www.b.com')
fetchb('./aaa').then(res=>res.json()).then(res=>console.log(res))
fetchb('./bbb').then(res=>res.json()).then(res=>console.log(res))
fetchb = null
}
</script>
3.10.2.闭包的应用
1.记住列表索引
<ul>
<li>001</li>
<li>002</li>
<li>003</li>
</ul>
<script>
var oli = document.querySelectorAll('li')
/*for(var i=0;i<oli.length;i++){
oli[i].onclick = function(){console.log(i)}//全是3,因为for循环已经执行完了,可以将var->let
}*/
for(var i=0;i<oli.length;i++){
oli[i].onclick = (function(index){
return function(){
console.log(index)
}
})(i)//构建匿名函数,直接马上调用,叫做匿名自执行函数
}
</script>
2.jsonp案例优化—函数防抖
<input type="text" id="mysearch" />
<ul id="list"></ul>
<script>
//之前的每输入一个字母就会发一次请求,这里优化为键入字母等待足够长的时间才发请求
function brisa(obj) {
console.log(obj)
list.innerHTML = obj.g.map((item) => `<li>${item.q}</li>`).join("");
}
//使用闭包实现
//设置一个定时器变量首先定义为null,随着字符输入,设置500毫秒延时发送请求,如果在这500毫秒内又输入了新的字符,则清除掉上一次的请求,否则则延时完成后发送请求
//且这里定时器要一直用,要保存下来,不被清除,但不建议将其改为全局变量,因为可能会名字冲突
//一般不建议多的去使用全局变量
mysearch.oninput = (function () {
var timer = null
return function (evt) {
if (timer) {//有值就清掉它
clearTimeout(timer);
}
timer = setTimeout(function () {//timer变为了1
// console.log("发送ajax请求");
if(!evt.target.value){
list.innerHTML = ''
return
}
var oscript = document.createElement('script')
oscript.src = `https://www.baidu.com/sugrec?pre=1&p=3&ie=utf-8&json=1&prod=pc&from=pc_web&sugsid=36547,37558,37520,38093,38126,38116,37989,37925,26350,38099,38008,37881&wd=${evt.target.value}&req=2&csor=1&cb=brisa&_=1675842510792`
document.body.appendChild(oscript)
oscript.onload = function(){
oscript.remove()
}
}, 500)
};
})();//自执行
</script>