js高级

165 阅读12分钟

Unit-01

一. 执行环境,变量对象以及作用域

1. 执行环境

  • js代码的执行环境:

  • 全局执行环境(默认)

  • 函数执行环境 ( 调用函数的时候, 就会创建函数的执行环境 )

  • js的执行环境,会被放入一个栈中, 默认栈中放的是全局执行环境, 当有函数调用, 就会创建函数的执行环境,且把这个执行环境推入栈中 ( 栈: 执行环境栈 或 调用栈 )

2. 变量对象

  • 每个执行环境,都会关联一个看不见的 变量对象
  • 变量对象,存着这个执行环境中的所有标识符
  • 标识符:变量名 / 函数名 / 形参 / 对象的键名
  • 当执行环境销毁, 关联的变量对象也会把所有标识符销毁

3. 作用域

3.1 概念:

  • 标识符可以使用的范围 (自己作用域和子作用域 )

3.2 作用域链

  • 提供了一套规则, 从里往外(函数声明的大括号)查找标识符, 找到就可以使用,找不到就报错

3.3 作用域分类

  • 全局
  • 局部(函数作用域)
  • 块作用域

二. 函数进阶

1. 函数的写法

  • 声明式写法

     function 函数名(形参) {
         // 执行代码
     }
     ```
    
    
  • 表达式写法

      const 变量 = function(形参) {
          // 执行代码
      }
      ```
    
    
  • 箭头函数

    • 形参有且只有1个,可省略() [ 也可以不省略 ] (不建议小括号)

    • 执行代码只有1句,可省略{}, 且必须省略return, 会自动return [ 也可以不省略 ]

    const 变量 = (形参) => {
    // 执行代码    
    }
    

2. 函数的参数

  • 形参与实参

    • 形参:占位使用, 要语义化, 叫啥都行
    • 实参:调用函数,实际传入的参数,传入的参数需要和形参一一对应
  • 默认参数:

    • es5写法

       function fn(param) {
           let temp = param || 默认值
       }
       ```
      
      
    • es6写法

       function fn(param = 默认值) {
           
       }
       ```
      
      
  • arguments

    o 在函数内部可以直接获取到, 获取到所有实参, 结果是:伪数组

    o 作用: 不能确定参数个数的情况

  • rest剩余参数

    • es6推出的, 叫: 剩余参数. 用来取代arguments
function 函数名(形参1, ...形参) {
   // 形参1: 得到实参1
   // 形参: 得到 [实参2, 实参3, 实参4]
}

函数名(实参1, 实参2, 实参3, 实参4)

3. 函数的返回值

  • 函数没有return, 默认return undefined

  • 函数返回啥,就得到啥.

  • 哪里调用函数, 函数的返回值,就返回到那里去.

  • 函数都要return吗?

    • 如果调用一个函数, 只是让它干一件事, 干就完了, 不要结果, 就不需要return

      渲染函数

      弹窗函数

    • 如果调用一个函数, 需要它把结果汇报给你, 要使用变量接收这个结果, 把结果用在别的地方.

      求和

      获取年月日

4. 函数的调用

  • 直接调用 ---直调

     function 函数名() {}
     函数名()
     
     const 变量 = function() {}
     变量()
     ```
    
    
  • 对象里的函数 -- 对调

     let obj = {
         函数名: function() {}
     }
     ​
     obj.函数名()
     ```
    
    
  • 回调函数: 当满足一定条件,自动调用执行 - 回调

     setTimeout(function() {
         console.log(666);
     }, 1000)
     
     dom.addEventListener("click", function() {
         console.log(888)
     })
     ```
    
    
  • IIFE立即执行函数表达式 - 自调

     ;(function() {
         // 执行代码
         console.log(666)
     })()
     ```
    

三. 闭包

1. 闭包的概念

  • 函数跨作用域访问变量,形成闭包. 闭包是一种作用域的体现. 一般把: 函数 和 变量 的总和,称为闭包

2. 闭包的两种标准写法

  • 父函数嵌套子函数, 子函数访问父函数的变量,把子函数返回或挂在全局
  • 第一种
;(function() {
    let 变量1;
    function 子函数() {
        console.log(变量1)
    }
    window.属性名 = 子函数  
})()
  • 第二种
const 变量 = (function() {
    let 变量1;
    function 子函数() {
        console.log(变量1)
    }
    return 子函数  
})();

3. 闭包的作用

  • 把变量封闭包裹在一个匿名函数内部, 隐藏变量或私有化变量, 实现早期的模块化

4. 闭包的缺点

  • 形成闭包的变量会常驻内存,不被js的垃圾回收机制回收。过多使用闭包,会造成内存开销过大,甚至内存泄漏

Unit-02

一. 面向对象和面向过程

  • 面向过程(POP): 按照流程, 一步一步的做, 所有步骤亲力亲为 ----小兵思维
  • 面向对象(OOP): 把功能封装成对象, 要做什么找对象,调用对象的方法, 指挥者的思维 ---将军思维
  • 类和实例对象
  • 类(构造函数): 一个大的分类: 数组 对象 字符串 数字 日期
  • 实例对象: 一个非常具体的对象: 小貂蝉 18
const 实例对象 = new 类()   
const arr = new Array([1,2,3]) 
const obj = new Object()

二. 构造函数

  • 在es5中, js没有类, 所以使用 构造函数代表类
  • JS有内置的构造函数(类)
// 实例化对象( 使用new 调用构造函数 创建出实例对象 )
const obj = new Object()
const arr = new Array()
const num = new Number()
const str = new String()
const date = new Date()
  • 构造函数的特点
  • 函数名大写字母开头
  • 属性和方法都挂在this上
  • 没有返回值
  • 使用new调用
 // 创建一个类:Person
        function Person(name, age, sex) {
            // 给this添加属性
            this.name = name;
            this.age = age;
            this.sex = sex;
            
            // 给this添加方法
            this.eat = function () {
                return "干饭"
            }
        }
  • new的过程

  • 创建一个空对象

  • 让this指向空对象

  • 执行构造函数代码

  • 自动返回this

// 创建一个类:Person
function Person(name, age, sex) {
    // 给this添加属性
    this.name = name;
    this.age = age;
    this.sex = sex;

    // 给this添加方法
    this.eat = function () {
        return "干饭"
    }
}

// p1, p2是返回的实例对象
let p1 = new Person("小貂蝉", 18, "女");
let p2 = new Person("赵子龙", 19, "男");

三. 原型和原型链

1. 方法过载

  • new出来的实例对象, 每个实例对象,都有一份自己的方法,称为 方法过载,内存开销过大

2. 原型 prototype

// 创建一个类:Person
        function Person(name, age, sex) {
            // 给this添加属性
            this.name = name;
            this.age = age;
            this.sex = sex;
        }

        // 向原型(原型是一个对象)添加方法
        Person.prototype.eat = function() {
            return "干饭"
        }
        
        const p1 = new Person("熊大", 18, "男")
        const p2 = new Person("熊二", 17, "男")
        console.log(p1);
        console.log(p2);
  • 每个函数,特指构造函数,都有一个属性prototype,就是原型( 显式原型 )
console.log("原型1:",Person.prototype);
  • 原型prototype是一个对象, 添加在里面的方法,被所有new出来的实例对象共享
console.log("原型2:",p1.eat === p2.eat) // 方法是共享的
  • 实例对象,都有一个属性__proto__(隐式原型), 等于自己构造函数的prototype(显式原型)
console.log("原型3:", p1.__proto__===Person.prototype, p2.__proto__===Person.prototype)
  • 原型prototype里面有一个属性constructor, 指向构造函数本身
console.log("原型4:",Person.prototype.constructor === Person)

注意:浏览器无法显示实例对象的__proto__属性,而是以[[Prototype]]代替__proto__

3. 原型的作用

  • 添加共享方法
Person.prototype.eat = function() {
            return "干饭"
}

4. 原型链

  • 原型prototype本身也是一个实例对象, 也有__proto__, 指向自己构造函数Object的prototype
console.log("原型5:",Person.prototype.__proto__===Object.prototype)
  • Object的prototype也是实例对象, 也有__proto__, 指向null
console.log("原型6:",Object.prototype.__proto__)
  • Object的prototype也有属性constructor, 指向构造函数Object本身
console.log("原型7:",Object.prototype.constructor === Object)

5. 原型链的作用

  • 添加共享方法
  • 实例对象查找方法,先找自己,自己没有,就沿着原型链往上找,找到就可以调用,直到Object.prototype找不到就报错

6. 图解

image.png

image.png

四. this

1. this的指向

  • 全局this: window
  • 函数中的this: 谁调用,指向谁
  • 对象方法中的this: 谁调用,指向谁
  • 构造函数中的this: 实例对象
  • 事件处理函数中的this: 事件源
  • 定时器中的this: window
  • 箭头函数中的this: 上一级 ( 箭头函数没有this, 它的this绑定定义函数时所处的作用域 )

2. 3个改变this指向的方法

  • call() 和 apply(): 功能一模一样, 唯一的区别是传递参数方式不同
# 语法
函数名.call(this指向, 实参1, 实参2)   // 调用函数 且 指定this

函数名.apply(this指向, [实参1, 实参2]) // 调用函数 且 指定this
  • bind():把函数的this绑定到一个目标对象,然后返回一个新函数, 此后,只要调用新函数, this就固定了,指向之前绑定的目标对象
const 新函数 = 函数.bind(this指向)

新函数()

Unit-03

一. 基本类型和引用类型

1. js的数据类型

  • 基本类型:string,number,null,undefined,boolean,symbol,bigint
  • 引用类型:object (object, function,array)

2. 数据类型的内存分配

  • 基本类型: 存放在 栈内存 中
  • 引用类型: 存放在 堆内存中, 栈中存它的 地址 , 地址指向堆中的数据

3. 变量的复制和参数的传递

  • 基本类型: 复制和传递的是 值的拷贝
  • 引用类型: 复制和传递的是 地址的拷贝

二. 深浅拷贝

1. 为什么需要深浅拷贝

  • 拷贝一个引用类型 的数据 ( 基本都是拷贝: 对象 ),如果直接赋值, 就会共享数据,互相影响

2. 浅拷贝

  • 只能拷贝对象的一层
  • 方法一: for in
   let obj1 = {
            name: "小貂蝉",
            age: 18,
            company: {
                name: "源码"
            }
    }


    let obj2 = {}
    for (let key in obj1) {
        // console.log(key, obj1[key]);
    //   {name: , age: }
        obj2[key] = obj1[key]
    } 

    obj1.name = "老貂蝉"
    obj2.name = "子龙"
    obj1.company.name = "源码-科技"
    
    console.log(obj1)
    console.log(obj2)
  • 方法二: - Object.assign()
let obj1 = {
    name: "小貂蝉",
    age: 18,
    company: {
        name: "源码"
    }
}


let obj2 = Object.assign({}, obj1);

obj2.name = "子龙"
obj2.age = 17

console.log(obj1)
console.log(obj2)

3. 深拷贝

  • 方法一: JSON.parse ( JSON.stringify(要拷贝的对象) )

  • 缺点:

  • 函数和undefined会丢失

  • Infinity 和 -Infinity 会变成null

  • date会从对象格式变成字符串格式

  • reg正则会变成空{}

let obj1 = {
            name: "小貂蝉",
            age: 18,
            company: {
                name: "源码"
            }
        }

        // 第一种方法 JSON.parse(JSON.stringify(要拷贝的对象)) ----  
        let obj2 = JSON.parse(JSON.stringify(obj1));
        
        obj1.name = "老貂蝉"
        obj1.company.name = "黑马"

        obj2.name= "子龙"

        console.log(obj1);
        console.log(obj2);
  • 方法二: _.cloneDeep() // 使用 lodash.js 进行深拷贝
 // 引用本地的lodash.js文件
	<script src="./lodash.js"></script>
    <script>
        let obj1 = {
            name: "小貂蝉",
            age: 18,
            company: {
                name: "源码"
            },

            show: function () {
                console.log("shower")
            },
            a: undefined,
            b: Infinity,
            c: -Infinity,
            date: new Date(),
            reg: /abc/
        }

        let obj2 = _.cloneDeep(obj1)

        console.log(obj1);
        console.log(obj2);
    </script>

三. 变量类型检测

1.typeof 检测基本类型

typeof "abc"  // string
typeof 12     // number
typeof undefined  // undefined

typeof null    // object

typeof true   // boolean
typeof Symbol()  // symbol
typeof 12n  // bigint

typeof {}  // object
typeof []  // object
typeof function() {} // function

// [] {}这个分不清

function each(param) {
 // 1.如果这个param 是数组的话,遍历 , +1 
    if(如果这个param 是数组的话) {}
 // 2.如果是对象的话,把对象的键值打印出来。 
    if(如果是对象的话,把对象的键值打印出来)
}

2. instanceof 检测引用类型

  • 只能用于检测引用类型, 基本类型用不了.
变量 instanceof 构造函数 
// 检测前面的变量 是不是 后面构造函数的实例对象

let arr = [];
arr instanceof Array;

let obj = {};
obj instanceof Object;

let fn = function() {};
fn instanceof Function;

3. Object.prototype.toString.call() 完美检测所有类型

  • 在Object的prototype原型上,有一个方法, 叫: toString, 可以检测所有变量的类型
Object.prototype.toString.call(变量)  
// 使用call调用 指定this 这个方法会输出this的类型信息

Object.prototype.toString.call("abc")
Object.prototype.toString.call(10)
Object.prototype.toString.call(undefined)
Object.prototype.toString.call(null)
Object.prototype.toString.call(false)
Object.prototype.toString.call(Symbol())
Object.prototype.toString.call(10n)

Object.prototype.toString.call([])
Object.prototype.toString.call({})
Object.prototype.toString.call(function fn() {})
  • 封装检测类型的函数
function getType(item) {
    return Object.prototype.toString.call(item).slice(8, -1);
}

四. ES5继承

  • 继承主要指的是类与类之间, 让子类继承父类的一切
  • 让子类继承父类所有属性
  • 让子类继承父类所有原型方法
  • 找回子类构造函数
// 定义一个父类
        function Person(name, age, sex) {
            this.name = name;
            this.age = age;
            this.sex = sex;
        }

        Person.prototype.eat = function() {
            return "干饭"
        }

        
        // 定义一个学生类
        function Student(name, age, sex, skill) {
            // 1. 继承所有属性
           Person.call(this,name, age, sex); 

            this.skill = skill;         
        }

        // 2. 继承所有原型方法
        Student.prototype = Object.create( Person.prototype );
        
        // 3. 找回构造函数
        Student.prototype.constructor = Student;

        Student.prototype.study = function() {
            return "学习"
        }
        let st1 = new Student("小红", 16, "女", "跳舞")
        

        // 定义一个程序员类
        function Iter(name, age, sex, hob) {
            // 1. 继承所有属性
            Person.call(this, name, age, sex);
            this.hob = hob;
        }

        // 2. 继承所有原型方法
        Iter.prototype = Object.create( Person.prototype );
        
        // 3. 找回构造函数
        Iter.prototype.constructor = Iter;

        Iter.prototype.abi = function() {
            return "CV"
        }

        let it1 = new Iter("华子", 22, "男", "抽华子")

Unit-04

一. 变量的解构赋值

1. 解构数组

  • 从数组中取出数据更方便简洁
# ES5
let arr = [1, 2, 3]
let num1 = arr[0];
let num2 = arr[1];
let num3 = arr[2];

# ES6
// 把数组的值 按照顺序 解构出来 赋值给对应的变量
let arr = [1, 2, 3]
let [变量1, 变量2, 变量3] = [1, 2, 3]  

2. 解构对象

  • 从对象中取出数据更方便简洁
# ES5
let user = {
    name: "zs",
    age: 30,
    sex: "女"
}

let name = user.name;
let age =  user.age;
let sex = user.sex;

# ES6
let {name, sex, age} = user; // 通过变量和键名的对应关系 取出对象选中的值 赋值给对应的变量.

3. 解构参数

  • 解构参数的本质,就是解构数组和对象
  • 参数是数组的情况
function fn([变量1,变量2,变量3]) {
   console.log(变量1,变量2,变量3) 
}

fn(["a", "b", "c"])
  • 参数为对象的情况
function fn({name, age}) {}
let obj = {
    name: "zs",
    age: 20
}
fn(obj)

二. 展开运算符

1. 展开字符串

const str = 'hello'

console.log( ...str )  // h e l l o 展开成1个1个的

// 字符串变数组
const arr = [...str]  // [h, e, l, l, o]

// 字符串变对象
const o = { ...str } // { 0: h, 1:e, 2: l, 3:l, 4: o }

2. 展开数组

const arr = ['a', 'b', 'c']
console.log(...arr) // a b c

// 浅拷贝数组
const arr1 = [...arr] 

// 合并数组
const arr2 = [1, 2, 3]

3. 展开对象

const user = {
    name: "小甜甜",
    age: 18,
    sex: "男"
}

console.log(...user) // 报错

// 浅拷贝对象
const user1 = {...user}

// 合并对象
const user0 = {name : "至尊宝", age: 600, sex: "猴", skill: "七十二变"}
const user2 = {...user, ...user0}

4. 展开伪数组

function sum() {
    
    console.log(...arguments)
    
    console.log([...arguments]) // 把伪数组变成了真数组
}

sum(1, 2, 3, 4)

三. 模板字符串

  • 解决字符串和js变量拼接问题
  • 可以换行
let str = `abc${表达式}def`

# 表达式 与 语句
表达式:一个表达式会产生一个值。它可写在赋值语句等号的右侧。
a + b; // 算出一个值
fn(); // 算出一个值
myvar; // 算出一个值

语句: if()  for()

四. 对象的简洁写法

# ES5 写法

let name = "小貂蝉";
let age = 18;
let sex = "女"

let user = {
    name: name,
    age: age,
    sex: sex,
    show: function() {
        console.log("shower")
    }
}

 #ES6 写法
let user1 = {
    name, // 键名和键值的变量同名 可以省略
    age,
    sex,
    show() {  //对象中的方法:可以省略: function以及
        console.log("shower")
    }
}

五. 类class

1.类的写法

  • ES5写类
// 定义一个Person类,使用构造函数实现
function Person(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.num = 10;
}

Person.prototype.eat = function() {
    return "干饭"
}

// 静态方法: 只能通过类名.方法(),实例对象无法访问
Person.way = function() {
            return "走路"
}

let p = new Person("子龙", 17, "男")
  • ES6写类
class Person {
    num = 10; // num会自动成为实例属性 ( 自动挂在new出来的实例对象上 )
    // 构造函数   
    constructor(name, age, sex) {
        this.name = name;
    	this.age = age;
    	this.sex = sex;
    }
    // 添加在原型上的方法
   	eat() {
        return "干饭"
    }
    // 静态方法: 只能通过类名.方法(),实例对象无法访问
    static way() {
        return "走路"
    }
}

let p = new Person("小貂蝉", 18, "女")

2.继承

  • ES5:寄生组合式继承
// 定义一个Person 作为父类,使用构造函数实现
function Person(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.num = 10;
}

Person.prototype.eat = function() {
    return "干饭"
}

// 静态方法: 只能通过类名.方法(),实例对象无法访问
Person.way = function() {
            return "走路"
}


// 定义子类 Student
function Student(name, age, sex, skill) {
    Person.call(this, name, age, sex); // 1.继承父类的属性
    this.skill = skill;
}

Student.prototype = Object.create(Person.prototype) // 2.继承父类原型的方法

Student.prototype.study = function() {
    return "学习"
}

Student.prototype.constructor = Student // 3.找回自己的构造函数

Student.way = Person.way; // 4.继承静态方法
  • ES6类的继承
class Person {
    num = 10;
    // 构造函数
    constructor(name, age, sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;

    }
    // 向原型上添加方法
    eat() {
        return "干饭"
    }
    // 静态方法: 只能通过类名.方法(),实例对象无法访问
    static way() {
        return "走路"
    }
}

class Student extends Person {
    constructor(name, age, sex, skill) {
        super(name, age, sex) // 1. super必须写在第一行 继承父类的所有属性
        this.skill = skill;
    }
    study() {
        return "学习"
    }
}

六. ES6的模块化

  • 一个js文件,就是一个模块,可以互相导入导出

1. node的模块化 (运行在node环境中)

// 导出(暴露)
module.exports = {
    fn,
    name,
}

//导入
const r = require(路径或者模块名)

2. ES6的模块化(运行在浏览器环境中)

  • 方式一
// 导出
export var num = 10;
export function fn() {}


// 导入
import { num,  fn} from '路径'
  • 方式二
// 导出
export default {}  
// 可以导出任意东西 但是基本上是对象居多
// 这种导出方式 一个js文件 只能使用1次

// 导入
import 变量 from '路径'

Unit-05

一. 同步和异步

1. 概念

  • 同步: 代码从上往下执行, 后面的代码, 必须等待前面代码执行完毕,才可以执行
for(let i = 0; i < 50000; i++) {
    console.log("排在队伍前的第一人")
}

console.log("第2人")
console.log("第3人")
console.log("第4人")
  • 异步: 代码从上往下执行, 如果遇到异步代码, 异步代码会先让开,等待所有同步代码执行完毕,最后才执行
setTimeout(() => {
    console.log("排在队伍前的第一人")
}, 5000)
console.log("第2人")
console.log("第3人")
console.log("第4人")

2. js中异步的场景

js的异步, 是通过回调函数实现的

  • setTimeout
        console.log(1);

        setTimeout(function() {
            console.log(2)
        }, 1000);

        setTimeout(function() {
            console.log(3)
        }, 1100);

        console.log(4)
  • ajax
console.log(1);

axios.get("https://mock.apifox.cn/m1/1187430-0-default/test1").then((res) => {
    console.log(2, res.data)
});

axios.get("https://mock.apifox.cn/m1/1187430-0-default/test2").then((res) => {
    console.log(3, res.data)
})

console.log(4)
  • 事件处理函数
console.log(1);
document.querySelector("#btn").addEventListener("click", () => {
    console.log(666);
})
console.log(2)

二. 回调地狱

1. 概念

  • 回调函数 嵌套 回调函数,嵌套层级太多,称为回调地狱
  • 注意: 回调地狱对于实现功能,是没有任何问题的, 缺点是: 代码丑/难以维护/可读性差

2. 示例代码

  • 什么情况下,会产生回调地狱: 当你要控制异步的顺序 ( 工作中,最常用的是, 控制ajax的顺序 )
// 1.先获取省份 ID
    axios.get("http://47.96.154.185:3701/api/shared/province").then((res) => {
        console.log(res.data);
        let first = res.data.data[0].id;
        
        // 2. 通过省份ID 调接口  取城市 ID
        axios.get(`http://47.96.154.185:3701/api/shared/city?provinceId=${first}`).then((res) => {
            console.log(res.data);
            let second = res.data.data[1].id;
            // 3. 把城市ID 提交给服务器 ---- 调接口
            axios.get(`https://mock.apifox.cn/m1/1187430-0-default/upload?cityId=${second}`).then((res) =>{
                console.log(res.data)
            })
        })
    })

三. Promise

1. 概念

  • Promise是一个类(构造函数), 是 ES6 新增的, 主要用来解决 异步问题, 让我们更好的控制 异步的顺序

2. 基本语法

// 创建一个Promise示例
const p =  new Promise((resolve, reject) => {
    // console.log("resolve", resolve);
    // console.log("reject", reject);
    // resolve(); // 从进行中 -> 解决
    // reject() // 从进行中 -> 拒绝
})

p.then((data) => {
    // 上一步调用resolve触发
    console.log(data);
}).catch((err) => {
    // 上一步调用reject触发
    console.log(err);
}).finally(() => {
    // 无论是调用resolve,还是reject都会触发。
    console.log("最后")
})

3. 原型方法

p.then(function() {
    // 从进行中 -> 解决
}).catch(function() {
    // 从进行中 -> 拒绝
}).finally(function() {
    // promise状态变化,无论是解决还是拒绝,都会执行
})

4. Promise解决回调地狱

  • 使用Promise解决回调地狱, 就是解决异步的顺序问题.( 把回调嵌套回调, 改成从上往下的代码 )
        // 创建一个promise实例
        const p = new Promise((resolve, reject) => {
            // 发送第一个 ajax
            axios.get("http://47.96.154.185:3701/api/shared/province").then((res) => {
                console.log(res.data);
                let first = res.data.data[0].id;
                resolve(first);
            })
        })
        
        p.
        then((data) => {
            console.log(666, data);
            return new Promise((resolve, reject) => {

                // 发送第二个 ajax
                axios.get(`http://47.96.154.185:3701/api/shared/city?provinceId=${data}`).then((res) => {
                    console.log(res.data);
                    let second = res.data.data[1].id;
                    resolve(second);
                })
            })
        })
        .then((data) => {
            console.log(777, data);
            return new Promise((resolve) => {
                // 发送第三个ajax
                axios.get(`https://mock.apifox.cn/m1/1187430-0-default/upload?cityId=${data}`).then((res) =>{
                    console.log(res.data)
                    reslove();
                })
            })
        })
        .then(() => {
            console.log(888);
        })

四. async和await

1. 概念

  • asyncawait 是 ES7 新增的两个关键字, 必须配合使用, 是异步的 终极解决方案
  • async: 声明函数内部,有异步操作
  • await: 等待Promise实例对象状态切换完毕. 得到结果
async function fn() {
  await xxx
}

const fn = async function () {
   await yyy;
}

const fn = async () => {
    await zzz;
}

2. async和await解决回调地狱

      async function uploadData() {
            // 发送第1个 ajax
            // axios.get() 返回了一个promise实例
            // 自动了resolve(后端的响应值)
            let r1 = await axios.get("http://47.96.154.185:3701/api/shared/province");
            console.log(r1.data);
            let first = r1.data.data[0].id;
            
            // 发送第2个 ajax
            let r2 = await axios.get(`http://47.96.154.185:3701/api/shared/city?provinceId=${first}`);
            console.log(r2.data);
            let second = r2.data.data[1].id;

            // 发送第3个 ajax
            let r3 = await axios.get(`https://mock.apifox.cn/m1/1187430-0-default/upload?cityId=${second}`);
            console.log(r3.data)
        }

        uploadData();

五. axios库的使用

1. ### 发送get请求

  • 方式一
axios.get("url?参数名1=参数值1&参数名2=参数值2")
    .then((res) => {
    	console.log(res.data)
	})
    .catch((err) => {
    	console.log(err)
	})
    .finally(() => {
    
	})
  • 方式二
axios.get("url", {
        params: {
            参数名1:参数值1,
            参数名2:参数值2
        }
	})
    .then((res) => {
    	console.log(res.data)
	})
    .catch((err) => {
    	console.log(err)
	})
    .finally(() => {
    
	})
  • 方式三
axios({
    url: "",
    method: "get",
    params: {}
})
.then((res) => {
    console.log(res.data)
})
.catch((err) => {
    console.log(err)
})
.finally(() => {
    
})

2. post的请求方式

  • 方式一
axios.post("url", {
            参数名1:参数值1,
            参数名2:参数值2
	})
    .then((res) => {
    	console.log(res.data)
	})
    .catch((err) => {
    	console.log(err)
	})
    .finally(() => {
    
	})
  • 方式二
axios({
    url: "",
    method: "post",
    data: {}
})
.then((res) => {
    console.log(res.data)
})
.catch((err) => {
    console.log(err)
})
.finally(() => {
    
})

3. 拦截器

  • 请求拦截: 发送出去之前,拦截
axios.interceptors.request.use((config) => {
   // 操作 config
   return config
})
  • 响应拦截: 接收到数据之前,拦截
axios.interceptors.response.use((response) => {
   // 操作 response
   return response
})

4. 并发多个请求

function 第一个ajax() {
 return axios.get('/user/12345');
}

function 第二个ajax() {
 return axios.get('/user/12345/permissions');
}

axios.all([第一个ajax(), 第二个ajax()])
.then(axios.spread( (r1, r2) => {
   // 两个请求现在都执行完成
}));