JavaScript 重点知识总结

一、数据类型

1. 变量

  • 用来存放数据,保存的数据可以修改
  • var 、 let 声明的就是变量

2. 常量

  • 用来存放数据,保存的数据不可修改
  • const 声明的就是常量
  • const声明常量的时候必须赋初始值
  • const 声明的值类型是不允许重新赋值的
  • const 声明的引用类型,在不进行重新赋值的前提下,可以进行修改

3. 变量和常量的本质

  • 无论是变量还是常量,其本身都是数据,也需要在内存中占用内存空间,保存在内存的栈结构分区中

4. 面试:let、const、var的区别

  • let 和 const 不允许重复声明,var是可以重复声明的
  • let 和 const 没有变量提升,必须先声明后使用;var 存在变量提升,可以先使用后声明
  • let 和 const 有块级作用域,var没有

5. JS数据类型

5.1 基本(值)类型

  • Number
  • String
  • Boolean
  • null
  • undefinded
  • Symbol:唯一的数据
  • BigInt:任意精度的整数

undefined 和 null 的区别

  • undefined:定义了但没有赋值;或者没有return的函数
  • null:定义了且赋值为null
    初始值赋值为null,表明现在数据不明,但将来会赋值为对象
    结束时赋值为null:让对象成为垃圾对象,被垃圾回收器回收
  • 值类型的传递 —— 传递值
    JavaScript 重点知识总结_第1张图片
  • 基本数据类型的值在栈空间中存储,如果修改了数据,则是把原来的值直接干掉,重新存放新的值

5.2 引用类型(object)

  • Object
  • Function
  • Array
  • Date
  • RegExp

当Number、String、Boolean调用方法或属性时,就会变为基本包装类型(引用类型)

  • 引用类型的传递 —— 传递的是引用(地址)
    JavaScript 重点知识总结_第2张图片

  • 引用数据类型的对象在堆空间中存储,该空间的地址在栈空间中存储,如果修改栈空间存储的地址,则指向发生变化,也叫引用发生了变化,此时是在堆空间中重新指向了一个新的内存空间(存储了一个新的对象)

6. typeof 运算符

语法:

  • typeof 变量名
  • typeof(变量名)
  • 判断变量中存储的数据类型
  • typeof可以判断出的类型:String、Number、Boolean、undefined、Symbol、function
    let a = 10
    console.log(typeof a) //number
    let b = 'hha'
    console.log(typeof b) // string
    let c = true
    console.log(typeof c) // boolean
    let d = Symbol(23)
    console.log(typeof d) // symbol
    let e 
    console.log(typeof e) // undefined
    let fun = function() {}
    console.log(typeof fun) // function
  • typeof只能判断出是引用类型(object),但判断不出具体是哪种引用类型
  • 即:typeof不能判断 object和array、object和null
    let f = null
    console.log(typeof f) // object
    let obj = {
      name:'dudu'
    }
    console.log(typeof obj) // object
    let arr = [2,3,12]
    console.log(typeof arr) // object
    let date = new Date()
    console.log(typeof date) // object

7. instanceof 运算符

语法

  • 对象 instanceof 类型
  • 判断当前实例对象是否属于某种数据类型
  • 判断右侧函数的显示原型是否在左侧对象的原型链上

8. === 运算符

  1. ===:称为等同符,当两边值的类型相同时,直接比较值,若类型不相同,直接返回false
  2. == :称为等值符:当等号两边的类型相同时,直接比较值是否相等,若不相同,则先转化为类型相同的值,再进行比较

类型转换规则:

  • 如果等号两边是boolean、string、number三者中任意两者进行比较时,优先转换为数字进行比较。
  • 如果等号两边出现了null或undefined,null和undefined除了和自己相等,就彼此相等

JavaScript 重点知识总结_第3张图片

注意:NaN==NaN //返回false NaN不等于任何值
在这里插入图片描述

  • 除了判断是否等于 ==null 外,其他一律用 ===

二、对象

1. 对象是什么

  • 生活中的对象:看得到、摸得着的具体的某个事物,如:正在看视频的这个手机
  • 对象(object)是键值对(k:v)的集合,表示属性和值得映射关系

2. 对象的创建方式

2.1 字面量的形式

    let obj = {
      name:'哈哈',
      age:10
    }

2.2 new Object() 的形式

    let obj = new Object()
    obj.name = '嘟嘟'

2.3 工厂函数的形式

function createObject(name,age){
      var obj = new Object()
      obj.name = name
      obj.age = age
      return obj
    }
    var obj1 =createObject('小明',10)
    var obj2 =createObject('小红',20)
    console.log(obj1,obj2)

2.4 构造函数的形式

function Person(name,gender){
      this.name = name
      this.gender = gender
      this.sayHi=function(){
        console.log('您好,我是:'+this.name)
      }
    }
    var per = new Person('小明','男')
    per.sayHi()
    console.log(per)
  • new 做的四件事
    1. 函数体内部自动创建一个空白对象
    2. 函数的上下文(this)会指向这个对象
    3. 函数体内的语句会执行
    4. 函数会自动返回上下文对象,即使函数没有return语句
      JavaScript 重点知识总结_第4张图片

2.5 通过 class 创建对象

  • class 形式中,构造器内的属性都是在实例上
  • class中的方法,如果是通过赋值符号定义的,则该方法在实例上,否则该方法是在原型上
    class Student{
      // 构造器,构造器中的属性都是在实例上的
      constructor(name,age,gender){
        this.name = name 
        this.age =age
        this.gender = gender
      }
      // 在原型上
      sayHi(){
        console.log(`您好,我是${this.name},今年${this.age}岁了,是${this.gender}`)
      }
      // 在实例上
      eat=()=>{
        console.log('吃东西啊')
      }
    }

    // 实例化
    const stu = new Student('dudu',18,'女')
    stu.sayHi()
    stu.eat()
console.log(stu)

JavaScript 重点知识总结_第5张图片

2.6 单例模式创建对象

  • 不管对象创建了多少次,但最终的对象都只有一个
  • 应用:轮播图插件、better-scroll插件
    function createObj() {
      let instance = null
      return function(name) {
       // 如果对象不存在,则新创建一个,如果已经存在则直接返回
        if(!instance) {
          // 创建一个对象
          instance = new Object()
          // 初始化对象属性
          instance.name = name
        }
        return instance
     }
    }
    var getObj = createObj()
    var obj1 = getObj('小明')
    var obj2 = getObj('小红')
    console.log(obj1,obj2) // {name:小明},{name:小明}
    console.log(obj1===obj2) // true
  • truly变量: !!a === true 的变量 ( 两次取反后为true )
  • falsely 变量:!!a === false 的变量
  • falsely 变量:0、NaN、''、null、undefined、false
  • 除以上falsely 变量之外都是 truly 变量
    JavaScript 重点知识总结_第6张图片

3. 对象的调用属性的方法

  1. 对象.属性或方法名字

  2. 对象['属性或方法名']

    什么时候使用 对象[属性名字] 的写法

    • 不确定属性名字是什么(属性名字是变量),此时方括号内部的属性名不加引号
    • 属性名不符合js命名规范的,此时方括号内部的属性名必须加单引号

4. 面试:谈谈你对面向对象的理解

  • 面向对象是一种编程的思想
  • 与之对应对应的是面向过程
  • 面向过程讲究的是凡是都需要自己亲力亲为,注重的是过程
  • 面向对象具有封装、继承、多态等特性
  • JS是属于基于面向对象的一门语言
  • 面向对象就是提出需求,然后根据需求分析出需要的属性和方法,然后抽取并定义出相应的构造函数,最终实例化这个对象,通过对象点方法或属性来使用

三、原型

1. 执行函数定义和执行函数

  • 执行函数定义:执行函数的定义,包含函数声明或者函数表达式两种形式的定义
  • 执行函数:函数内部的语句被执行
  • 先有函数定义的执行,才有执行函数
    /**
     * 定义函数(函数声明+函数内部语句)
    */
    // 函数定义方式一 :函数声明
    // 执行函数定义
    function fun() {
      console.log('我是一个方法')
    } 
    // 函数的调用 —— 执行函数(函数内部的语句被执行)
    fun()
	
	 // 函数定义方式二:函数表达式
    const fun1 = function() {}

常见的函数回调

  • DOM事件的回调
  • 定时器中的回调
  • ajax回调函数
  • 生命周期回调函数
  • IIFE(立即执行函数 | 匿名函数自调用):隐藏内部实现,减少命名空间污染

2. 原型的产生

  • 执行函数定义时就会产生显示原型(prototype),而函数本身也是一个实例对象,所以同时也会产生隐式原型(__ proto__)【注:谷歌中的__proto__显示为 [[prototype]]
    function fun() {
      console.log('我是一个方法')
    } 

    console.dir(fun)

JavaScript 重点知识总结_第7张图片

3. 面试:谈谈你对原型的理解

  • 原型包含prototype 和 __ proto__,原型可以节省内存空间、实现数据共享、实现继承
  • 在执行函数定义的时候,显示原型prototype就会被创建,在实例化对象的时候,隐式原型 __ proto__就会被创建
  • 因为函数本身也是对象,所以函数除了显示原型prototype外,也拥有隐式原型__ proto__

4. 原型是什么

  • 在创建函数时,解析器会自动为函数添加一个属性prototype(原型),这个属性对应着一个对象称为原型对象
  • 原型是函数所特有的,任何函数对象都有显示原型prototype
  • 如果函数作为普通函数调用,则prototype无意义
  • 若作为构造函数调用,它所创建的对象(实例)中都会有一个隐含的属性,指向该构造函数的原型对象,可以通过 __ proto__ (隐式原型)访问该隐含属性
    function Fn() {}
    var obj = {}
    // obj是对象(object),会有隐式原型__proto__,所以obj的__proto__和Object的prototype相等
    console.log(obj.__proto__ === Object.prototype) //true
    console.log(Fn.prototype.__proto__ === Object.prototype) //true
  • prototype:浏览器的标准属性,程序员使用的,显示原型,存在于函数中
  • __proto __:浏览器的非标准属性,浏览器使用的,隐式原型,存在于实例对象中
  • __ ptoto__是在实例化对象时出现的
  • 实例的 __ proto__ 与对应函数的 prototype 都指向原型对象
    JavaScript 重点知识总结_第8张图片

5. 原型的作用

5.1 共享数据,节省内存空间

function Person(name,age) {
      this.name = name
      this.age = age
      this.eat = function(){
        console.log(`我是${name}实例中的eat方法`)
      }
    }
    Person.prototype.sayHi = function() {
      console.log('我是原型上的方法')
    }
    const dudu = new Person('嘟嘟',16)
    const heihei = new Person('黑黑',19)
    dudu.eat()
    heihei.eat()
    dudu.sayHi()
    heihei.sayHi()

JavaScript 重点知识总结_第9张图片

  • 在构造函数中定义的属性及方法,仅仅是编写代码进行定义而已,而实际上里面定义的属性及方法是属于每个实例对象的
  • 所以,创建多个对象,就会开辟多个空间,每个空间中的每个对象都有自己的属性及方法,大量创建对象,对象的方法都不是同一个方法(方法也是函数,函数代码也占用空间)
  • 通过将属性或方法绑定在原型上,就可以实现所有实例的数据共享,进而节约内存空间

JavaScript 重点知识总结_第10张图片

5.2 通过改变原型实现继承

  • 方法的继承关键语句:子类.prototype = new 父类() ,这样可以使用父类的方法
  • 如果子类要定义自己的方法,则应该写在关键语句之后
// 父类
    function Person(name,age) {
      this.name = name
      this.age = age
    }
    Person.prototype.sayHi = function() {
      console.log(`您好,我是${this.name},今年${this.age}岁了`)
    }
    
    // 子类
    function Student(name,age,studentID) {
      this.name = name
      this.age = age
      this.studentID = studentID
    }

    // 实现继承的关键语句
    Student.prototype = new Person('黑黑',23)
        
    const stu = new Student('嘟嘟',18,134287)
    stu.sayHi() //您好,我是嘟嘟,今年18岁了
  • 继承属性父类名.call(要继承的属性名),这样就可以使用父类的属性
    // 父类
    function Person(name,age) {
      this.name = name
      this.age = age
    }
    Person.prototype.sayHi = function() {
      console.log(`您好,我是${this.name},今年${this.age}岁了`)
    }
    
    // 子类
    function Student(name,age,studentID) {
      Person.call(name)
      Person.call(age)
      this.studentID = studentID
    }

    // 实现继承的关键语句
    Student.prototype = new Person('黑黑',23)
        
    const stu = new Student('嘟嘟',18,134287)
    stu.sayHi() // 您好,我是黑黑,今年23岁了
  • 组合继承:方法继承和属性继承统称为组合继承

6. 原型链

  • 原型链:隐式原型链,从对象的__ proto__开始,连接所有对象
  • JavaScript规定:实例可以打点访问它的原型的属性和方法,这被称为“原型链查找
  • 当访问对象的一个属性或方法时,它会优先在对象自身寻找,如果没有则会去原型对象找
    JavaScript 重点知识总结_第11张图片

6.1 hasOwnProperty()

  • 使用 in检测对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
console.log('sayHi' in stu) // true
  • 使用 hasOwnProperty()检测对象自身是否含有某属性,只有对象自身含有该属性才会返回true
console.log(stu.hasOwnProperty('sayHi')) // false

6.2 原型链的终点

  • 原型对象也是对象,所以也有原型
  • 当使用一个对象的属性或方法时,首先会先在对象自身寻找
    • 当自身没有,就沿着__ proto__ 在原型对象中寻找
    • 如果原型对象中没有,则会寻找原型的原型中,直到找到Object对象的原型(一般只会查找两层)
    • Object对象的原型没有原型,如果在Object中仍然没有找到,则返回undefined

6.4 原型对象的constructor

  • prototype属性值是对象,它默认拥有constructor属性指向对应的构造函数
function sum(a,b) {
        return a+b
    }
    console.log(sum.prototype.constructor === sum) //true

7. instanceof

  • 判断对象的具体类型
  • 所有的函数都是 Function 的实例对象,所以都会有隐式原型 __proto __
  • 所有的对象都是 Object 的实例对象

A instanceof B

  • A是实例对象,B是构造函数
  • 如果B的 prototype 属性所指向的原型对象是 A 原型链上的某个对象,则返回 true ,否则返回 false
 // 构造函数
    function Foo() {}
    // 实例对象
    const f1 = new Foo()
    // 实例对象
    const f2 = new Foo()
    // 实例对象
    const o1 = new Object()
    // 实例对象
    const o2 = {}
    
    console.log(Foo instanceof Object); // true
    console.log(Foo instanceof Function); // true
    console.log(Object instanceof Object); // true
    console.log(Function instanceof Function); // true
    console.log(Object instanceof Foo); // flase
    console.log(f1 instanceof Function);  // false
    console.log(f1 instanceof Object); // true

JavaScript 重点知识总结_第12张图片

8. 继承(改变原型指向 | 借用构造函数 | 组合 | 拷贝)

8.1 基于构造函数的继承:原型链 + 借用构造函数的组合继承

  • 借用父类型构造函数(继承属性): 父类.call()
  • 让子类的原型为父类的实例(继承方法): 子类.prototype = new 父类()
  • 让子类型原型的构造器为子类型: 子类.prototype.constructor = 子类

8.2 基于 class/类 的继承

  • 通过extends 关键字实现继承:class 子类 extends 父类
  • 继承父类的属性:super(属性名,属性名1,属性名2,...)

四、预解析与作用域

1. JS中的预解析

  • 先找关键字 var 、function函数声明
  • 找到var 后将 var 后的变量提前声明,但是不赋值(变量提升)
  • 找到 function后将function后面的函数提前声明,但是不赋值,即函数在解析之前已经定义完毕了(函数提升)
  • 函数表达式的定义提升时会报错
fun()
// 函数表达式
const fun = function() {} 

在这里插入图片描述

2. 执行上下文

  1. 执行上下文(动态的):就是一个代码的执行环境(全局执行上下文和函数执行上下文,eval函数执行上下文),包含:执行环境,变量对象,this,作用域链
  2. 执行上下文的流程:
    • js引擎在js代码正式执行前会先创建一个执行环境
    • 进入该环境以后会创建一个变量对象,该对象用于收集:变量,函数,函数的参数,this
    • 找关键字var,function
    • 确认this
    • 创建作用域链
  3. 在全局代码执行前,js引擎就会创建一个栈来存储管理所有的执行上下文
  4. 在全局执行上下文(window)确定后,将其添加到栈中(压栈)
  5. 在函数执行上下文创建后,将其添加到栈中(压栈)
  6. 在当前函数执行完毕后,将栈顶的对象移除(出栈)
  7. 当所有的代码执行完毕后,栈中只剩下window
  8. 重点:执行上下文是动态创建的,尤其是针对函数,每调用一次函数都会创建一次执行上下文

面试:谈谈你对执行上下文的理解

  • 当代码要执行,但是没有执行,或者将要执行,在预解析之后,
  • 此时出现了全局执行上下文环境(全局执行上下文),
  • 创建了一个变量对象,用来收集var , function ,函数参数,确定this的指向,
  • 默认全局执行上下文是确定了this是window,
  • 这个变量对象会被压入到栈中(全局执行上下文的变量对象在栈中的最下面),
  • 如果出现了函数调用,此时出现了局部执行上下文环境(局部执行上下文),
  • 再次创建一个变量对象,用来收集函数参数,var ,function,改变this的指向,
  • 这个变量对象会被再次压入栈中,在全局执行上下文的变量对象的上面,
  • 如果当前函数调用完毕,此时出栈(把局部上下文的变量对象干掉),依次弹出变量对象,就结束了

3. 作用域

3.1 作用域

  • 作用域:某个变量的合法使用范围
  • 全局作用域:变量在函数外定义,即为全局变量。全局变量有 全局作用域: 网页中所有脚本和函数均可使用
  • window对象下的内置属性都是全局作用域
  • 函数作用域:写在函数内部的变量,就只能在函数内部使用
  • 块级作用域:写在花括号 {} 内部的变量
  • 常见的块级作用域:function(){}、for(){}、while(){}、do{}while()、if(){}、switch(){}
  • 对象虽然有花括号,但不是块级作用域

当代码书写完毕后,全局作用域就被确定了
在执行代码之前,会先预解析,此时全局的执行上下文出现
调用函数之前,局部作用域就确定了,局部作用域中预解析执行,此时局部的执行上下文就确定了

3.2 自由变量

  • 一个变量在当前作用域内没有定义,但被使用了
  • 向上级作用域,一层一层的依次寻找,直到找到为止
  • 如果在全局作用域中也没有找到,则报错 xxx is not defined
  • 自由变量的查找,是在函数定义的地方,向上级作用域查找,而不是在执行的地方查找!

五、 闭包

1. 闭包产生的条件

  • 函数之间存在嵌套关系
  • 调用了外部函数
  • 内部函数引用了外部函数的数据(变量/函数)
  • 则内部函数在执行函数定义时,就产生了闭包。
  • 闭包也是对象

2. 闭包的作用

  • 延长了局部作用域的生命周期,但如果不及时释放会出现内存泄露
  • 让函数外部可以操作(读写)函数内部数据(变量/函数)

3. 内存泄露 和 内存溢出

  • 内存泄露:程序在申请内存后,无法释放已申请的内存空间就会造成内存泄露。虽然一次泄露不会有太大影响,但内存泄露堆积的后果就是内存溢出
  • 内存溢出:程序在申请内存时,没有足够的内存供申请者使用

4. 闭包的释放

  • 让内部函数成为垃圾对象,断开指向它的所有引用(比如将外部函数赋值为null)

5. 常见的闭包

  • 闭包是作用域应用的特殊情况,有以下两种情况(函数定义和执行的地方不一致)
    • 函数作为参数被传递
    • 函数作为返回值被返回
  • 闭包的特性:函数会记住定义时所处的环境,查找变量时会从定义时的位置开始向上级查找

6. 闭包的应用

  • 如在删除列表中某个商品时

7. 面试:谈谈你对闭包的理解

答题方向:闭包的产生、是什么、优缺点、应用

  • 如果函数之间存在嵌套关系,外部函数被调用,且内部函数中使用了外部函数的数据(变量或函数),此时在内部函数执行函数定义时,就会产生闭包
  • 闭包就相当于是一个容器对象
  • 闭包延长了局部函数的声明周期,让函数外部可以操作函数内部的数据
  • 但闭包如果不及时释放,就会造成内存泄露
  • 常见的闭包包含将函数作为返回值或者参数进行传递
  • 比如,在后台管理系统中经常会用到的删除操作,当需要删除某个商品时,需要传入商品id

六、 this指向

1. 判定规则

  1. 规则一:对象打点调用函数时,this是打点的对象

对象.方法()

var obj1 = {
       a:1,
       b:2,
       fn() {
           console.log(this.a+this.b)
       }
   }
   var obj2 = {
       a:3,
       b:4,
       fn:obj1.fn
   }
   obj2.fn() // obj2调用的,所以this是obj2。然后函数也是引用类型,进行赋值的时候会指向同一个堆,所以会打印出 7
function outer() {
       var a = 20
       var b = 12
       return {
           a:33,
           b:44,
           fn() {
               console.log(this.a+this.b)
           }
       }
   }
   outer().fn() // outer()返回一个对象,所以最后也是构成了对象.方法的调用,this就会指向返回的对象,所以会打印出77
  1. 规则二:圆括号直接调用函数,则this是window对象

函数()

var obj1 = {
       a:1,
       b:2,
       fn() {
           console.log(this.a+this.b)
       }
   }
   var a = 3
   var b = 4
   var fn = obj1.fn
   fn() //通过函数直接调用,this指向window对象,则结果会返回7
function fn() {
       return this.a + this.b
   }
   var a = 1
   var b =2
   var obj = {
       a:3,
       b:fn(),
       fn:fn
   }
   var result = obj.fn() //通过对象的形式调用,this指向obj对象,a=3,b:fn()相当于全局调用了fn方法,b中的this指向window,所以b=3
   console.log(result) // 6
  1. 规则三:数组(类数组对象)枚举出函数进行调用 ,this是这个数组(类数组对象)

数组 [ 下标 ] ( )

  • 类数组对象:所有的键名为自然数序列(从0开始)且有length属性的对象
  • arguments对象是最常见的类数组对象,它是函数的实参列表
function fun() {
       arguments[3]()
   }
    fun('A','B','C',function() {
        console.log(this[1]) // this指向arguments类数组对象'A','B','C',function(),结果输出B
    })

4 规则四:IIFE中的函数,this是window对象

(function() {

})()

var a = 1
    var obj = {
        a:2,
        fun:(function(){
            var a = this.a; //IIFE中的this指向window,则a=1
            return function() {
                // a 在函数体内部,则a=1
                // return 返回了一个函数
                console.log(a + this.a) // 3
            }
        })()
    }
    obj.fun() //点的方式调用函数,则this指向obj对象,则this.a=2
  1. 规则五:定时器、延时器调用函数,this是window对象

setInterval(函数,时间)
setTimeout(函数,时间)

var obj = {
        a:1,
        b:2,
        fun() {
            console.log(this.a + this.b) 
        }
    }
    var a = 3
    var b = 4
    setTimeout(obj.fun, 2000); //this指向window,则this.a=3,this.b=4
    setTimeout(() => {
        obj.fun() //对象点方法的形式调用函数,则this指向obj对象,this.a=1、this.b=2
    }, 2000);
  1. 规则六:事件处理函数的this是绑定事件的DOM元素
DOM元素.onclick = function() {
//当函数内部有其他函数,比如定时器时,要考虑this的指向
// 必要时需要备份this,即 var _this = this,这时在内部的函数中就可以用_this指向需要的DOM
 }
  1. 规则七:call和apply可以指定 this 的指向

函数.call(上下文)
函数.apply(上下文)

  1. 规则八: 构造函数中,this指向实例对象

  2. 规则九: 箭头函数中,this会向上级作用域寻找

JavaScript 重点知识总结_第13张图片

2. 如何控制函数的 this

  • 利用函数的 bind()
  • 利用箭头函数
  • 利用外部保存了this的变量

七、异步 和 单线程

  • 同步:必须等待上一步执行完毕后,下一步才可以执行
  • 异步:在等待上一步执行的同时,可以进行其他处理
  • 进程:程序的一次执行,它占有一片独有的内存空间
  • 线程:CPU的基本调度单位,是程序执行的一个完整流程
  • JS是单线程的

1. 同步任务和异步任务

1. 同步任务

  • 非耗时任务,指主线程上排队执行的任务
  • 同步会阻塞代码执行
  • 只有前一个同步任务执行完成,才能进行下一个同步任务
  • 构造函数属于同步任务
    2. 异步任务
  • 耗时任务,指由js委托给**宿主环境**(js的执行环境,如浏览器、node.js等)进行执行
  • 异步不会阻塞代码
  • 当异步任务执行完成后,会通知JavaScript主线程执行异步任务的**回调函数 callback**

2.宏任务和微任务

异步任务 分为宏任务和微任务

2.1 宏任务

  • 异步Ajax请求
  • setTimeout、setInterval
  • DOM 事件监听
  • 文件操作
  • 其他宏任务
  • 宏队列:用来存放宏任务的容器

2.2 微任务

  • Promise.then、.catch、.finally
  • process.nextTick
  • async、await
  • 其它微任务
  • 微队列:用来存放微任务的容器

2.3 执行顺序

  • 页面第一次渲染:初始化同步代码 ==> 所有的微任务 ==> 渲染界面 ==> 执行第一个宏任务 ==> 所有的微任务 ==> 渲染界面 ==> 执行第一个宏任务
  • 界面更新渲染:所有的微任务 ==> 界面渲染 ==> 第一个宏任务

3. 事件轮询机制(event loop,异步实现的原理)

  • JS是通过事件轮询机制来实现单线程异步的
    JavaScript 重点知识总结_第14张图片
    JavaScript 重点知识总结_第15张图片
    JavaScript 重点知识总结_第16张图片

4. 面试:谈谈JS的执行机制

  • JS是单线程的,是同步去执行的,但是JS也可以通过事件轮询机制进行异步操作
  • 首先会顺序执行所有的同步任务,异步任务会被委托给宿主环境
  • 已完成的异步任务对应的回调函数,会被放到任务队列中排队
  • JS再从任务队列中读取异步任务的回调,但会优先读取所有的微任务
  • 这个过程会不断重复

5. Promise

  • Promise 可以解决回调地狱问题(可阅读性差,任然需要回调,但是不会嵌套)
  • Promise指定回调的时机更加灵活(可以指定在异步操作启动后或完成后)

5.1 promise的理解

  • promise是一个**构造函数**,可以通过new创建一个实例,new出来的Promise实例对象就代表一个异步操作
  • promise的原型对象(prototype) 上包含一个 .then() 方法
  • then()方法用来指定成功和失败的回调函数

fun.then(成功的回调,失败的回调)

5.2 promise 的状态

  • pending:进行中
  • fulfilled/resolved:成功
  • rejected:失败

状态的改变不可逆,只能从进行中变为成功或失败

5.3 promise.then的理解

  • then() 会返回一个新的 promise
  • 新promise的结果状态是由then指定的回调函数执行结果决定的
    • 抛出错误:失败的reason就是抛出的错误
    • 返回失败的promise
    • 返回成功的promise
    • 返回其他值
    • 返回一个非Promise值
  • 抛出错误的情况
new Promise((resolve,reject) => {
      // 成功的回调
      // resolve('成功!')
      // 失败的回调
      reject('失败~')
    }).then(val => {
      console.log(val)
    },err => {
      console.log(err)
      // 抛出错误
      throw 100
    }).then(val => {
      console.log('success '+val)
    },err => {
      console.log('failed '+err)
    })

在这里插入图片描述

  • 返回失败的回调
    new Promise((resolve,reject) => {
      // 成功的回调
      // resolve('成功!')
      // 失败的回调
      reject('失败~')
    }).then(val => {
      console.log(val)
    },err => {
      console.log(err)
      // 抛出错误
      // throw 100

      // 返回失败的回调
      return Promise.reject(200)
    }).then(val => {
      console.log('success '+val)
    },err => {
      console.log('failed '+err)
    })

在这里插入图片描述

  • 返回成功的回调
    new Promise((resolve,reject) => {
      // 成功的回调
      // resolve('成功!')
      // 失败的回调
      reject('失败~')
    }).then(val => {
      console.log(val)
    },err => {
      console.log(err)
      // 抛出错误
      // throw 100

      // 返回失败的回调
      // return Promise.reject(200)

      // 返回成功的回调
      return Promise.resolve(300)
    }).then(val => {
      console.log('success '+val)
    },err => {
      console.log('failed '+err)
    })

在这里插入图片描述

  • 返回其他值
    new Promise((resolve,reject) => {
      // 成功的回调
      // resolve('成功!')
      // 失败的回调
      reject('失败~')
    }).then(val => {
      console.log(val)
    },err => {
      console.log(err)
      // 抛出错误
      // throw 100

      // 返回失败的回调
      // return Promise.reject(200)

      // 返回成功的回调
      // return Promise.resolve(300)

      // 返回其他值
      return 1000
    }).then(val => {
      console.log('success '+val)
    },err => {
      console.log('failed '+err)
    })

在这里插入图片描述

5.4 Promise.all()方法

Promise.all([Promise1,Promise2,…]) 方法可以把多个Promise实例包装为一个新的Promise实例

只有需要包装的多个Promise实例状态都为成功,新的Promise状态才为成功(等待机制)

const promiseArr = [
    thenFs.readFile('./files/1.txt','utf-8'),
    thenFs.readFile('./files/2.txt','utf-8'),
    thenFs.readFile('./files/3.txt','utf-8')
]

Promise.all(promiseArr)
        .then(result => {
            console.log(result) //[ '第一个文件', '第二个文件', '第三个文件' ]
        })

5.5 Promise.race()方法

Promise.race([promise1,promise2,…])用法和all()类似,
但是只要其中任何一个异步完成,就会立即执行下一步的.then()操作(赛跑机制)

Promise.race(promiseArr)
        .then(res => {
            console.log(res) //第一个文件
        })

6. 谈谈对Promise的理解

  • promise可以用来解决回调地狱的问题,promise指定回调的时机是灵活的,可以指定在异步操作启动后或者完成后
  • promise有pending执行中、resolved成功、reject失败三种状态,且只能从进行中到成功或失败,是不可逆的
  • promise的then会返回一个新的promise

7. async/await

  • async/await是ES8引入的新语法,用于简化Promise异步操作
  • 如果某个方法返回的是Promise实例,就可以使用await
async function getAllFile() {
   const res1 = await thenFs.readFile('./files/1.txt','utf8')
   console.log(res1)
   const res3 = await thenFs.readFile('./files/3.txt','utf8')
   console.log(res3)
   const res2 = await thenFs.readFile('./files/2.txt','utf8')
   console.log(res2)
}
getAllFile()
  • 如果方法内部使用了await则该方法必须被async修饰
  • 在async方法中,第一个await之前的方法会同步执行,await之后的方法会异步执行

你可能感兴趣的:(JavaScript,javascript,前端)