【02】js基础知识点整理

1、js数据类型

JS的数据类型分为两大类,基本数据类型(String、Number、Boolean、Null、Undefined、Symbol) 和 引用数据类型 (Object)。基本数据类型是保存到栈内存中的,而引用数据类型是保存在堆内存中的(严格来讲,是由栈区和堆区共同保存,栈区保存变量标识符和指向堆内存的地址)。

数据类型划分

1> 基本数据类型 (6种): 基本数据类型的数据存放在栈中

String、Number、Boolean、Null、Undefined、Symbol(ES6)

复制变量:将一个变量复制给另一个变量时,会将变量值的副本赋值给新变量,此后两个变量完全独立

2> 引用数据类型 (Object - 在js中除了基本数据类型以外都是对象,Array、Function、Object、RegExp都是 对象):

引用类型是同时保存在栈区中和堆区中的,引用类型的存储需要在内存的栈区和堆区中共同完成,栈区保存变量标识符和指向堆内存的地址

复制变量:将引用数据类型的变量复制给另一个变量时,会将当前变量的内存地址赋值给新变量,此时两个变量都指向内存中同一个对象

typeof类型检测

可以判断所有的基本数据类型,null会被判定为object;引用类型除了function可以判断,其余都为object。

1> 定义:返回表示当前数据类型的字符串
2> 语法:typeof(表达式) | typeof 变量名
3> 返回值:number、string、boolean、undefined、symbol、object、function
返回值object代表,对象类型的变量或值,或者null(这个是js历史遗留问题,将null作为object类型处理)

深拷贝

深拷贝不会拷贝引用类型的引用,而是将引用类型的值全部拷贝一份,形成一个新的引用类型

实现方法:

1> JSON.stringify(obj1) -> JSON.parse(obj1Str)

将JSON对象转换为字符串进行拷贝,拷贝成功后将字符串转为JSON对象,此方法只可以拷贝JSON格式的对象

2> Object.assign({}, obj1)

将source源对象枚举值复制一份到目标对象target,只可以拷贝一层, 当source属性是其他引用类型,还是只拷贝了引用

3> 递归拷贝 deepClone(obj)

/**
 * 深拷贝
 * @param {Object} obj 要拷贝的对象
 */
function deepClone(obj = {}) {
  // obj不是引用类型直接返回 (typeof null为object,需要排除)
  if(typeof obj != "object" || obj == null) {
    return obj
  }
				
  // 判断当前obj是数组还是对象 (判断obj是否是数组构造函数的实例)
  let result = obj instanceof Array ? [] : {}
  
  for(let key in obj) {
    // 保证 key 不是原型的属性 object.hasOwnProperty(propertyName)
    if(obj.hasOwnProperty(key)) {
      // result是数组时赋值: result[index] -> result[key]
      // result是对象时赋值:result.key (此种方式适用于key是键值名时) | result[key] (key值不确定,key为一个变量)
      // 递归调用deepClone
      result[key] = deepClone(obj[key])
    }
  }
  
  return result
}

类型转换

string
1> .toString()方法:不可以转null 和 undefined
2> String()方法:都能转
3> 隐式转换: num + ‘’

number
1> Number()方法:把任意值转换为数值,如果要转换字符串中存在不是数值的字符,返回NaN
2> parseInt()方法:遇到非数字结束;如果第一个字符不是数字就返回NaN
3> parseFloat()方法:parseFloat会解析第一个. 遇到第二个. 或者非数字结束;如果解析的内容里只有整数,解析成整数
4> 隐式转换:str-0,结果为number类型

boolean
1> Boolean()方法:0 “” null undefined NaN 会转为false, 其余为true
2> 隐式转换:!str、!boolean、!nummber 等

俩等比较 和 三等比较

俩等比较:弱比较。在进行比较时,如果变量类型不一致,会自动进行一次类型转换
三等比较:强比较。先进行类型比较,如果数据类型不一致,直接返回false

俩等只有在 判断等于null 或 undefined 时,才会等价于三等;推荐使用三等

2、数组的方法

10个基本方法

(1) arr.join(拼接符) : 数组转字符串,拼接符默认为逗号;不会改变原数组
(2) arr.push() | arr.pop():数组末尾添加一个或多个元素,返回新长度 或 删除并返回数组最后一个元素;会改变原数组
(3) arr.unshift() | arr.shift():数组开头添加一个或多个元素,返回新长度 或 删除并返回数组第一个元素;会改变原数组
(4) arr.sort():数组排序,默认按照字符串编码顺序排序,需要添加排序函数;会改变原数组

排序函数规则:俩个形参a、b,并且通过冒泡的方式比较;当返回值为负数时,那么前面的数在前面,也就是不动;当返回值为正数时,那么后面的数在前面

升序: arr.sort((a, b) => a - b)
降序: arr.sort((a, b) => b - a)

(5) arr.reverse():反转数组顺序;会改变原数组
(6) arr.concat():拼接数组,创建当前数组副本,将参数添加到末尾,返回新数组;不会改变原数组
(7) arr.slice(start, end[可选]) :数组截取,返回一个新数组 包含从start - end(不包含该元素)的元素;不会改变原数组

正数代表从数组头部开始,0代表第一项;负数代表从数组尾部开始,-1代表尾部第一项

start必需
end可选,未指定时,代表从start位置到数组结束位置

(8) arr.splice(index, num, item…):删除、 插入和替换,返回值为含有被删除元素的数组,若没有删除元素返回空数组;会改变原数组

正数代表从数组头部开始,0代表第一项;负数代表从数组尾部开始,-1代表尾部第一项

index必需,删除|插入项目的开始位置
num必需,要删除元素的数量;设置为0, 不会删除元素
item可选,向数组添加的新元素

总结:
改变原数组:arr.push()、arr.pop()、arr.unshift()、arr.shift()、arr.sort()、arr.reverse()、arr.splice()
不改变原数组:arr.join()、arr.concat()、arr.slice()

8个ES6新增方法

(1) arr.map((item, index) => { return newItem }):返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值;不会改变原数组
(2) arr.forEach((item, index) => {}):调用数组的每个元素,循环一遍;修改item会改变原数组
(3) arr.filter((item, index) => { return Boolean }):返回一个新数组,数组中的元素是通过检查原数组中符合条件的所有元素;不会改变原数组
(4) arr.some((item, index) => { return 条件 }) | arr.every((item, index) => { return 条件 }):不会改变原数组

arr.every()只有所有项都满足条件,才会返回true;arr.some()只要有一项满足条件, 就会返回true

(5) arr.indexOf() | arr.lastIndexOf():数组索引,返回查找项在数组中首次出现位置, 没找到返回-1;不会改变原数组

arr.indexOf(item, [start])从数组开头开始;start可选,默认为0第一项
arr.lastIndexOf(item, [start])从数组末尾开始;start可选,默认为array[array.length-1]开始检索

(6) arr.reduce((prev, cur, index, arr) => {}, [initialValue]):数组求和;不会改变原数组

reduce()接受一个函数作为累加器,从数组第一个值开始缩减,直到最后一个值缩减完成。最终计算为一个值

参数介绍
(prev, cur, index, arr) => {}回调函数
prev:初始值initialValue,或fun的返回值
cur:arr数组的当前累加项
index:当前累加项的索引
arr:要累加的arr非空数组
initialValue初始值;当没有初始值时,回调函数第一次累加的prev是arr的第一项,cur是arr的第二项,index的值为1

总结:
改变原数组: arr.forEach()
不改变原数组: arr.map()、arr.filter()、arr.some() 、arr.every()、 arr.indexOf()、arr.lastIndexOf()、arr.reduce()

3、字符串的方法

字符串的所有方法都不会改变原字符串

15个基本方法

(1) str.charAt(index) :返回指定索引位置的字符;index的取值范围为0 ~ str.length-1,等价于str[index]
(2) str.charCodeAt(index):返回指定索引位置字符的 Unicode 值
(3) str.split(切割符):将字符串以切割符分割为数组
(4) str.concat() :字符串拼接;类似与 + 拼接
(5) str.slice(start, [end]) | str.substring(start, [end]) | str.substr(start, [length]):字符串截取,返回一个字符串 包含从start - end(不包含该元素)的元素

end 或者 length 未指定时,代表从str的start位置截取到str结束
slice() 比 substring()要灵活一些,允许使用负数作为参数

str.slice(start, [end])

正数代表从str头部开始,0代表第一项;负数代表从str尾部开始,-1代表尾部第一项
起始字符要比结束字符小,否则截取不成功

str.substring(start, [end])

俩个参数都必须必须是非负整数
如果start比end大,那么该方法在提取字符串之前会先交换这两个参数

str.substr(start, [length])

ECMAscript未标准化,不建议使用
正数代表从str头部开始,0代表第一项;负数代表从str尾部开始,-1代表尾部第一项

(6) str.replace(reg/oldstr, newstr):替换字符串,返回替换后的字符串

reg需要设置全局 g 标志, 否则只替换第一个

(7) str.indexOf(str, [start]) | str.lastIndexOf(str, [start]):字符串索引,返回检索字符在字符串中首次出现位置,没有找的返回-1

str.indexOf(str, [start])从字符串开头开始;start可选,默认为0第一项
str.lastIndexOf(str, [start])从字符串结尾开始;start可选,默认从array.length-1开始检索

(8) str.match(reg) | str.search(reg/str):字符串检索

str.match(reg)返回str中与正则或字符串相匹配的子串, 匹配不到返回null
若reg没有g标志,只匹配查找的第一项,返回值为一个数组;arr.index可获取到匹配项在字符串中索引
若reg有g标志,全局匹配;返回结果只为包含匹配结果的数组


str.search(reg/str)返回str中第一个与正则或字符串相匹配的子串的起始位置,匹配不到返回-1
str.search()不执行全局匹配,会忽略g

(9) str.toLowerCase() | str.toUpperCase():大小写转换
(10) str.trim():去除字符串首尾空白

4个es6新增方法

(1) startsWith() | endsWith():用以检测字符串是否以另外一个给定的子字符串开头或者结尾,并根据判断结果返回 true 或 false
(2). padEnd(targetLength, [str]) | padStart(targetLength, [str]) :字符串填充, 返回填充后达到指定长度的字符串;str可选,默认使用空格补全长度

4、对象

名词解释

(1) 对象:对象代表一个具体的事物,对象由特征和行为组成;js中的对象是对现实对象的抽象,由属性和方法组成
(2) 面向对象:面向对象是一种编程方式, 是对面向过程式编程的封装;面向对象的特性有封装、继承、多态
(3) 原型对象:
1> 每个构造函数都有一个prototype属性,该属性指向一个对象,包含了通过调用该构造函数所创建的对象共享的属性和方法
2> 则prototype就是通过该构造函数创建的某个实例的原型对象

ES5对象

创建对象

(1) 对象字面量形式

对象方法中访问对象的属性:通过 this.name 来访问对象的属性, this代表当前对象
访问对象属性:obj.name | obj[‘name’]
访问对象的方法:obj.func()

var obj = { name: 'sky', func: function() { console.log(this.name) } }

(2) Object构造函数形式

new Object() 相当于调用构造函数,执行时会在内存中创建一个对象

var obj = new Object()
obj.name = 'sky'
obj.func = function() { console.log(this.name) }

(3) 工厂模式

缺点:使用工厂函数创建的对象无法判断具体类型,只能判断出数据类型为Object

// 先创建工厂函数,再使用工厂函数创建对象

function create(name) { 
  let obj = new Object()
  obj.name = name
  obj.func = function() { console.log(this.name) }
  return obj
}

var obj1 = create('Tom')

(4) 自定义构造函数模式

可以判断对象类型,例子中对象的类型为Create

// 先创建自定义构造函数Create,再使用自定义构造函数创建对象
function Create(name) {
  this.name = name
  this.func = function() { console.log(this.name) }
}

var obj1 = new Create('Tom')

遍历对象

(1) 遍历:for (var key in obj) {}
(2) 获取对象的属性名: key代表obj中属性名称
(3) 获取对象的value值:通过 obj[key] 访问属性对应的值

删除对象的属性

delete obj.name

判断对象的类型

(1) typeof:引用数据类型除了function可以判断,其余都为object,无法判断出具体类型
(2) constructor:obj.constructor、[].constructor 等

不建议使用, constructor可以被改变
比如,Create.prototype = {} 时,重写了prototype,当前prototype已经没有constructor,会去上级找
需要重新设置constructor:Create.prototype = {constructor: Create, …}

(3). instanceof:判断当前对象是否是某个构造函数的实例

用法:obj instanceof Object 、 arr instanceof Array 、 obj instanceof 自定义构造函数名称 等

构造函数的原型对象(prototype)

(1) 含义:prototype 指通过构造函数创建的实例的原型对象
(2) 定义: 构造函数名称.prototype.name 、 构造函数名称.prototype.func
(3) 使用:当实例访问属性和方法时,会先去实例本身找,没有再去原型对象prototype中找
(4) _proto_:用来访问对象的原型对象,非标准属性;对象._proto_ = 构造函数.prototype
(5) constructor:prototype上的一个属性,记录了当前原型对应的构造函数

实例可以通过 obj.constructor 获取创建当前实例的构造函数

(6). Object.getPrototypeOf(obj)

该方法是ES5中用来获取obj对象的原型对象的标准方法,等价于obj._proto_
Object.getPrototypeOf(obj) === obj._proto_ === Object.prototype

构造函数、对象 、原型对象的关系

(1) 构造函数通过 new 创建对象
(2) 构造函数拥有原型对象,通过prototype属性访问和设置原型对象
(3) 对象通过__proto__属性指向构造函数的原型对象
(4) constructor(构造函数):原型对象的constructor属性,指向原型对象对应的构造函数

在这里插入图片描述
原型链

查找对象的属性方法时,会先去对象自身找, 没有再去对象的原型对象找;没有再去原型对象的原型对象找

(1) 构造函数的prototype也是一个对象,由Object构造函数创建,则构造函数的prototype的原型对象是 Object.prototype

Create.prototype._proto_ == Object.prototype
obj._proto_._proto_ == Object.prototype

(2). 原型对象的最顶端为null

obj._proto_._proto_._proto_ == null

面向对象特性

封装
客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

继承
可以让某个类型的对象获得另一个类型的对象的属性的方法,继承的本质是代码复用

(1) 原型继承 prototype

实用性不高,无法设置继承的构造函数的参数

// Parent构造函数
function Parent() {
  this.name = 'test'
  this.age = 18
  this.func = function() { console.log(this.name) }
}

// Children构造函数
function Children() {
  this.sex = 'gril'
}

// 通过原型对象让children继承Parent.prototype中的方法
Children.prototype = new Parent()
Children.prototype.constructor = Children

// 创建child实例
let child = new Children()

(2) 借用构造函数 call | apply

借用构造函数只能继承父类型的属性,不能继承父类型的方法

function fn(a, b) {
  console.log(this, a+b)
}
let a = { name: 'ceshi' }

// 1、bind(this, params):改变函数fn的this指向, 并返回一个新的函数, 自身不会调用函数
let f = fn.bind(a, 2, 4)
f() // {name: 'ceshi'}, 6

// 2、 call(target, params):改变函数fn的this指向,直接调用函数
// Children构造函数中写入代码;Parent.call(this, params);借用Parent构造函数动态传参
fn.call(a, 8, 1) // {name: 'ceshi'}, 9

// 3、apply(target, [params]): 改变函数fn的this指向, 直接调用函数
// Children构造函数中写入代码;Parent.apply(this, [params]); 借用Parent构造函数动态传参
fn.apply(a, [1,2]) // {name: 'ceshi'}, 3

(3) 组合继承

原型继承只能继承方法,不能设置继承的构造函数的参数;借用构造函数只能动态传参,不能继承构造函数的方法。将原型继承和借用构造函数组合:
1> 借用构造函数继承属性:Parent.call(this, params)、Parent.apply(this, [params])
2> 原型继承方法:Children.prototype = new Parent(); Children.prototype.constructor = Children;

多态
同一个方法,在不同的对象中,有不同的表现,就是多态;比如打印机都有打印方法,彩色打印机的打印是彩色的,普通打印机是黑白的
1> 必须要有继承
2> 必须要有方法的重写

class Animal {
  constructor (name) {
    this.name = name
  }
 
  eat () {}
}
 
class Cat extends Animal {
  constructor (name) {
    super(name)
  }
 
  eat () {
    console.log('吃鱼')
  }
}
 
class Dog extends Animal {
  constructor (name) {
    super(name)
  }
 
  eat () {
    console.log('吃骨头')
  }
}
 
let c = new Cat("猫")
let d = new Dog('狗')

ES6对象(语法糖)

构造函数class

创建:通过 class 关键词创建一个类,每个class都包含一个constructor构造函数
使用:通过new关键词创建对象
继承::class 通过 extends 关键词实现继承 (子类会继承父类的方法)

// 创建
class Father {
  // constructor方法是类的构造函数,是一个默认方法,通过 new 命令创建对象实例时,自动调用该方法。
  // 一个类必须有 constructor 方法,如果没有显式定义,一个默认的 consructor 方法会被默认添加。
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  func() { alert(this.name) } 
}

// 使用
let obj = new Father('rose', 18)

// 继承
class Children extends Father {
  constructor(name, age, sex) {
    // 如果子类中要使用this关键字,就必须在construtor里面执行super()方法
    // 子类的constructor构造函数中通过super关键字获取父类的属性
    super(name, age)
    this.sex = sex
  }
  
  speek() { console.log(this.name, this.age, this.sex) } 
}

let obj2 = new Children('rose', 18, "男")

对象新方法

(1) Object.is():用来判断俩个值是否相等,要求俩个值的数据类型和值完全相同

基本等价与es5中的严格相等运算符(===);仅有区别是 +0 和 -0不相同,NaN 等于 NaN
Object.is(NaN, NaN) 返回true
Object.is(+0, -0) 返回false

(2) Object.assign(target, …suorce):用来将可枚举值的属性和方法拷贝到当前对象中, 浅拷贝(引用类型只拷贝一层, 后续层还是共用同一个内存空间)

ES6对象字面量语法扩展

(1) 属性简写:当属性名称和属性值的变量名称相同时, 可以省略冒号的变量名称

{ age: age, name: name } 等价于 {age, name}

(2) 方法简写: es6可以省略冒号和函数关键字

{ fun: function() {} } 等价于 { fun(){} }

(3) 计算属性名:对象字面量中的属性名可以计算,用方括号表达式法进行计算,方括号中的值为表达式,最终结果为表达式计算的结果

let name = 'ceshi'; { [name]: 'test' }
let name = 'ceshi'; { [`first ${name}`]: 'test' }

5、js作用域 与 闭包

作用域分类

(1) 全局作用域
(2) 函数作用域
(3) 块级作用域(es6新增):块级作用域实际上就是 {}

自由变量

在当前作用域使用未被定义的变量,该变量称为自由变量
变量会像上级作用域一层一层查找,到找到为止
到全局作用域还没有找到, 报错 xx is not defined
特殊情况 (自由变量的查找,是在函数定义的地方向上级查找,不是在函数执行的地方)

闭包

什么是闭包?

闭包简单理解就是,外部函数可以访问内部函数的变量
闭包的本质是,形成一个命名空间,对外暴露一些私有的属性和方法,供外部进行调用。

了解

比如说,我们在html中引入里a.js 和 b.js,他们俩个是拥有同一个全局作用域的,所有定义在全局的变量都是通用的
此时a.js中有一个变量a,b.js是可以访问到这个变量a,并对其进行修改的,这达不到变量私有化的目的
可以使用自调函数,就会形成一个单独的命运空间,但此时自调函数之外的和b.js中都是访问不到这个变量a的
如果使用闭包,b.js中可以访问到变量a,同时引用的a.js也不会和自己的代码发生冲突
a.js中的数据无论引用到哪里,都是隔离的;同时他提供了返回函数作为api,让其他地方以指定的方式访问数据
其他地方不需要关心数据具体是什么逻辑、怎么操作,也不用担心会和自己的代码产生冲突

闭包的形成

闭包是作用域应用的特殊情况,原因是自由变量的查找,是在函数定义的地方向上级查找,不是在函数执行的地方

(1). 函数作为参数被传递 (了解)

let a = 100
function fn(func) {
  let a = 200
  func()
}

function fun1() { 
  console.log(a)
}

fn(fun1) // 执行结果 100

(2) 函数作为返回值被返回 (常见)

function fun1() {
  let a = 100
  return function() {
    console.log(a)
  }
}

let fn = fun1()
let a = 200
fn() // 执行结果 100

闭包的缺点

闭包是比较耗费内存的,闭包会让函数内部变量始终保存在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收,会造成内存泄露

闭包的应用

(1) 隐藏数据:只提供api对数据进行操作(可以避免数据污染)

function create() {
  // 闭包中的数据
  let data = {}
  return { 
    set(key, val) {
      data[key] = val
    }, 
    get(key) {
      return data[key]
    }
  }
}

let test = create()
test.set("a", 100)
console.log(test.get("a"))

(2) 创建10个 a 标签, 点击时弹出对应序号

错误写法:
在绑定事件时,事件还未开始执行;事件在点击时,开始执行,当前的i已经变为10
变量i 的作用域是全局作用域,console.log(i) 输出的 i 是自由变量,此时i为10

for(var i = 0; i < 10; i++) {
  var a = document.createElement("a")
  a.innerHTML = i
  a.addEventListener("click", () => { console.log(i) })
  document.body.appendChild(a)
}

解决方法:
1> 使用let关键字声明变量,此时声明的变量的作用域是块级作用域
2> 自执行函数:不用调用,只要定义完成,立即执行

for(var i = 0; i < 10; i++) {
  (function(i) {
    var a = document.createElement("a")
    a.innerHTML = i
    a.addEventListener("click", () => { console.log(i) })
    document.body.appendChild(a)
  })(i) 
}
						}

函数中的this指向

this的取值,是在函数执行的过程中确定的,不是在函数定义时确定的

我们可以通过call() 、 apply() 、 bind() 方法来修改函数的this指向

(1) 作为普通函数被调用,this指向window
(2) 作为对象方法被调用时,this指向当前对象
(3) 在构造函数中(es5, es6的class方法),this指向当前创建的实例对象
(4) 箭头函数,this取上级作用域的this,箭头函数本身不会决定this的值
(5) 定时器中函数的this指向window,定时器中函数相当于普通函数被调用(setTimeout | setInterval)

6、异步

异步的应用场景

网络请求:如ajax、图片加载 img.onLoad = ()=>{}
定时任务:如定时器(setTimeout | setInterval)

异步处理方式

(1) callback

通过callback处理异步逻辑时,是一种嵌套形式的书写方法,代码逻辑会非常复杂,会形成回调地狱

$.get(url1, data1 => {
  $.get(url2, data2 => {
    ...
  })
})

(2) promise:

以链式调用方式处理异步逻辑(串连形式),代码逻辑只有一层,基于回调函数

function getData(url) {
  return new Promise((resolve, reject) => {
    $.ajax({
      url,
      success(data) { resolve(data) },
      error(err) { reject(err) }
    })
  })
}
// return普通数据时, then中接收的是该数据; return promise对象时, then中接收的是promise对象的resolve函数传递的数据
// 一般不这样写, 只是举例
getData(url1).then(data1 => {
  return getData(url2)
}).then(data2 => {
  return data2 
}).then(data2 => {

}).catch(err => {

})

event loop(事件循环)

什么是 event loop ?

JS是单线程运行的,异步是要基于回调来实现
evnet loop(事件循环/事件轮询)是计算机系统的一种运行机制,是异步回调的实现原理

js运行机制

(1) js在运行时,分为俩个线程:主线程,负责程序本身运行;任务队列(task queue),负责处理异步任务
(2) 所有的同步任务都在主线程执行,形成一个执行栈(stack);异步任务会存放在Web APIs中,当异步任务(定时器、ajax等)有了执行结果后,会将回调结果存放在任务队列中(task queue)
(3) 一旦执行栈(stack)中的同步任务执行完毕, 然后event loop就会开始循环查找task queue,将异步任务的回调放入执行栈中,开始执行。(先执行当前的微任务,再尝试DOM渲染)
(4) 主线程不断重复第三步

在这里插入图片描述
.
.
DOM事件和Event loop

DOM事件执行原理基于Event loop
(1) js在执行时,将DOM事件的回调函数存放在Web APIs中,当用户发生操作DOM的行为时,会将该事件的回调结果放入任务队列(task queue)中
(2) 根据eventloop机制,将回调结果放入执行栈stack中,开始执行

微任务和宏任务

微任务的触发时机是早于宏任务的,因为微任务是在DOM渲染前执行,宏任务是在DOM渲染后执行
微任务在执行时, 不会经过Web APIs, 会放在micro task queue(微任务队列中)

(1) 微任务:promise、 async/await
(2) 宏任务:ajax、定时器、DOM事件等

Promise高级应用

promise的三种状态

响应状态(pending)、成功状态(resolved)、失败状态(rejected)

pending状态可以转换为两种状态(pending -> resolved、pending -> rejected),变化不可逆;pending状态,不会触发then和catch
resolved状态,会触发后续的then回调函数
rejected状态,会触发catch回调函数

then、catch 对状态的影响

then 正常时返回状态为 resolved,有报错时返回状态为rejected
catch 正常时返回状态为 resolved,有报错时返回状态为rejected
then和catch的状态都会触发后续的链式调用函数(如果有写),具体触发then/catch函数由状态判断

Promise.resolve(true).then(data => {
  console.log(data)
}).catch(() => {}).then(() => {
  console.log("test")
})
// 执行结果: true test,最终状态为 resolved

Promise.resolve(true).then(data => {
  console.log(data)
  throw new Error("error")
}).catch(err => { console.log(2) }).then(() => {
  console.log("test")
})
// 执行结果: true 2 test,最终状态为 resolved

Promise.all()、Promise.race()

Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。

let p1 = new Promise((resolve, reject) => {
	resolve('111')
})
let p2 = Promise.reject('222')
let p3 =  Promise.resolve('333')
let result = Promise.all([p1, p2, p3]) // 参数为 promise 对象组成的数组
console.log(result) // 222
let p1 = new Promise((resolve, reject) => {
	resolve('111')
})
let p2 = Promise.resolve('222')
let p3 =  Promise.resolve('333')
let result = Promise.all([p1, p2, p3]) // 参数为 promise 对象组成的数组
console.log(result) // ['111', '222', '333']

Promise.race就是赛跑的意思,意思就是说,Promise.race([runAsync(3,‘洗漱’),runAsync(2,‘上床’), runAsync(1,‘睡觉’)])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

let p1 = new Promise((resolve, reject) => {
	resolve('111')
})
let p2 = Promise.resolve('222')
let p3 = Promise.resolve('333')
let result = Promise.race([p1, p2, p3]) // 参数为 promise 对象组成的数组
console.log(result) // 111

async/await

(1) async/await: async/await是一个语法糖,异步的本质上还是回调函数;用同步的方式来写异步,代码的书写彻底抛弃回调
(2) 执行async函数返回的是一个promise对象(如果return的是普通数据test, 等价于Promise.resolve(“test”))
(3) await等价于promise的then, await后面可以跟promise对象、async函数、普通数据(等价于 Promise.resolve(“test”))
(4) 使用try…catch捕获异常, 代替了promise中的catch

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值