ES6新特性

实际上JavaScript是ECMAScript的扩展语言,ECMAScript只提供了最基本的语法。2015年开始ES保持每年一个版本的迭代。ECMAScript2015也叫es6,开发者喜欢用es6泛指所有新标准
在这里插入图片描述

1、let与块级作用域

块级作用域,凡是带{}的都是一个作用域
1、在内部定义的变量a,在外部依旧可以访问

if(true){
    var a = '123' 
}
 console.log(a)  // 123

2、作用域内部重新定义外部变量,则不会对外部变量进行修改,循环体有两层嵌套的作用域,

if(true){
    let a = '123' 
    console.log(a) //可以访问到
}
 console.log(a)  // 报错 访问不到
for (var i = 0;i<3;i++) {
	for(var i = 0;i<3;i++){
		console.log(i) // 1,2,3  因为内层循环结束后i是3影响外面的i 所以外层退出循环,只执行一次,解决办法var改成let
	}
}

2、const

用来声明常量,相比于let增加了只读属性。变量声明之后不可以被更改。
1、const生成的常量不允许被修改

const a = 'test'
a = 'test2'  //不允许被修改

2、const生成的对象不允许修改指向的内存地址,但是可以修改内部属性

const obj = {}
obj.name = '123' //内部属性允许修改
console.log(obj)
obj = {} //指向的内存地址不允许修改,报错

项目中尽量不用var,避免变量提升以及垃圾作用域,主用const(默认全部使用const),配合使用let(如果值需要修改,则使用let)

手写实现一个const功能

var __const = function __const (data, value) {
        window.data = value // 把要定义的data挂载到window下,并赋值value
        Object.defineProperty(window, data, { // 利用Object.defineProperty的能力劫持当前对象,并修改其属性描述符
          enumerable: false,
          configurable: false,
          get: function () {
            return value
          },
          set: function (data) {
            if (data !== value) { // 当要对当前属性进行赋值时,则抛出错误!
              throw new TypeError('Assignment to constant variable.')
            } else {
              return value
            }
          }
        })
      }
      __const('a', 10)
      console.log(a)
      delete a
      console.log(a)
      for (let item in window) { // 因为const定义的属性在global下也是不存在的,所以用到了enumerable: false来模拟这一功能
        if (item === 'a') { // 因为不可枚举,所以不执行
          console.log(window[item])
        }
      }
      a = 20 // 报错

3、解构赋值

ES6允许按照一定的模式,从数组和对象中提取值,对变量进行赋值,这个过程被称之为解构

//完全解构
const arr = [1,2,3]
const [arr1,arr2,arr3,arr4] = arr
const [arr1,arr2,arr3,arr4 = '123'] = arr //设置默认值,如果取不到值,则会使用默认值
console.log(arr1,arr2,arr3) //1 2 3
console.log(arr4)//undefined,就像是访问数组中不存在的下标

const str = 'foo/boo/too'
const [a,b,c] = str.split("/")
log(a,b,c)  // foo boo too

不完全解构,数组中的部分值对应到相应的变量,不取模式匹配的值需要使用逗号隔开

//不完全解构
const arr = [1,2,3]
const[,,arr4] = arr
console.log(arr4)  // 3

对象的解构,没有的值也会打印undefined,也可设置默认值

//对象解构
const obj = { name:'test', age:18}
const {name} = obj
console.log(name)

可设置别名

const {name:myName} = obj //myName,匹配完name以后将值赋值给myName,
console.log(myName) // test

4、字符串模板

const name = 'tom'
const str = `name is ${name}`
console.log(str) // name is tom
const str1 = `运算:${1+1}`
console.log(str1) // 2

字符串的扩展方法startWith、endsWith、includes

const message = 'my name is tom'
console.log(
    message.startsWith('my'), //true 
    message.endsWith('123'), //false
    message.includes('is') //true
)

5、默认参数

function foo(str,name = 123){ //直接在name参数后添加默认值123,注意:设置默认值的参数一定要放到最后
    console.log(str,name)
}
foo('str')
foo('str','hello')

6、剩余参数

function foo(...arg){
    console.log(arg) // [1,2,3,4]
}
foo(1,2,3,4)

7、展开数组

在这里插入代码片const arr = [1,2,3,4]
console.log(...arr) //1 2 3 4

8、箭头函数

出现的作用除了让函数的书写变得很简洁,可读性很好外;最大的优点是解决了this执行环境所造成的一些问题。比如:解决了匿名函数this指向的问题(匿名函数的执行环境具有全局性),包括setTimeout和setInterval中使用this所造成的问题

const add1 = (n1,n2) => n1+n2
console.log(add1(3,4))  // 7

9、箭头函数和this对象

this对象是在运行时基于函数的执行环境绑定的:在全局函数中,this指向的是window;当函数被作为某个对象的方法调用时,this就等于那个对象

函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。箭头函数外面的 this 是什么,箭头函数内的 this 还是什么。

10、对象字面量的增强

const bar = 'bar'
const obj = {
    name:123,
    bar, //等同于:bar:'bar'
    myLog(){
        console.log(this) //指向当前对象obj
    },
    [1+1+'str']:'12' //等同于 str2:‘12’,[]中可计算,计算的结果 作为属性名
}

11、Object.assign()

const target = {a:456,c:789}
const source1 = {a:123,b:456}
const result = Object.assign(target,source1) //有返回值,返回值与第一个参数target相同
console.log(target) //{ a: 123, c: 789, b: 456 }
console.log(result === target)  // true
console.log(result === source1)  // false

12、Object.is()

比较两个值是否完全一致

console.log(NaN===NaN)//false
console.log(+0===-0) //true

console.log(Object.is(NaN,NaN)) //true
console.log(Object.is(+0,-0)) //false

13、Proxy

const person = { name:'test',age:12} //定义

const personProxy = new Proxy(person,{
  get(target,property){
    return property in target ? target[property]:'default' //返回值
  },
  set(target,property,value){
    if(property === 'age'){
        if(!Number.isInteger(value)){
            throw new TypeError("age not a num")  //可以对传递的数据进行校验
        }
    }
    target[property] = value
    console.log(target,property,value)
  }
})

console.log(personProxy.name) //test
console.log(personProxy.qq) //default
personProxy.name='hello'
personProxy.age='12'

在这里插入图片描述
Proxy相对于Object.defineProperty有什么优势呢

  1. 优势1:Proxy 可以监视读写以外的操作 比如:删除
   const person = {
     name: 'zce',
       age: 20
   }

   const personProxy = new Proxy(person, {
     deleteProperty (target, property) {
       console.log('delete', property) // age
       delete target[property]
   }
   })

   delete personProxy.age
   console.log(person)
  // person = {
    // name: 'zce',
  // }
  1. 优势2:Proxy 可以很方便的监视数组操作
 const list = []
 const listProxy = new Proxy(list, {
     set (target, property, value) {
       console.log('set', property, value) // 
       target[property] = value
       return true // 表示设置成功
     }
   })

   listProxy.push(100)

  1. 优势3:Proxy 不需要侵入对象
// defineProperty方式
 const person = {}

 Object.defineProperty(person, 'name', {
   get () {
     console.log('name 被访问')
     return person._name
   },
   set (value) {
     console.log('name 被设置')
     person._name = value
   }
 })
 Object.defineProperty(person, 'age', {
   get () {
     console.log('age 被访问')
     return person._age
   },
   set (value) {
     console.log('age 被设置')
     person._age = value
   }
 })

 person.name = 'jack'

 console.log(person.name)
 
//Proxy 方式更为合理
const person2 = {
name: 'zce',
age: 20
}

const personProxy = new Proxy(person2, {
get (target, property) {
  console.log('get', property)
  return target[property]
},
set (target, property, value) {
 console.log('set',target, property, value)
target[property] = value
}
})

personProxy.name = 'jack'

console.log(personProxy.name)

14、Reflect

静态类,通过类型.方法名调用,Reflect成员方法就是Proxy处理对象的默认实现

const myObj = {
    name:'Tom',
    age:18
}
console.log(
    Reflect.has(myObj,'name'), // true
    Reflect.deleteProperty(myObj,'name'), // true
    Reflect.ownKeys(myObj) // ['age']
)

15、构造函数

//es5
   function Person (name) {
     this.name = name
   }

   Person.prototype.say = function () {
     console.log(`hi, my name is ${this.name}`)
   }
   // es6
class Person {
constructor (name) {
  this.name = name
}
say () {
 console.log(`hi, my name is ${this.name}`)
}
}
const p = new Person('tom')
p.say()

16、静态方法

static logMethond(){
        console.log('我是静态方法')
}
Person.logMethods() // ‘我是静态方法’

17、类的继承

// extends 继承
class Person {
  constructor (name) {
    this.name = name
  }

  say () {
    console.log(`hi, my name is ${this.name}`)
  }
}

class Student extends Person {
  constructor (name, number) {
    super(name) // 父类构造函数
    this.number = number
  }

  hello () {
    super.say() // 调用父类成员
    console.log(`my school number is ${this.number}`)
  }
}

const s = new Student('jack', '100')
s.hello()

18、set

Set是一种数据结构,用来存放不重复的数据,常用来去重

  1. 创建与添加元素,add()方法返回一个set对象,所以可以链式调用add
const set = new Set()
set.add(1).add(2).add(3).add(4) //添加元素


  1. 遍历
//第一种方式
set.forEach(element => {
    console.log(element)
});
//第二种方式
for(let i of set){
   console.log(i)
}


  1. 获取长度,size
console.log(set.size)


  1. 判断是否含有某个元素,has
console.log(set.has(1)) // true


```js

 5. **删除某个元素,delete**
 

```js
set.delete(1)


  1. 清除集合中的数据,clear
set.clear()


  1. set与array的转化
//将set转化为数组
let arr = [...set]
let arr1 = Array.from(set)
//将数组转化为set
let arr = [1,2,3,4]
let set1 = new Set(arr)
console.log(set1)


18、WeakSet

WeakSet只能存放对象类型,不能存放基础类型;WeakSet对元素引用是弱引用,没有其他引用的会被回收,比如当 对象 = null。这也意味着WeakSet中没有存储当前对象的列表。 正因为这样,WeakSet 是不可枚举的


const ws = new WeakSet();
const obj = {};
const foo = {};

ws.add(window);
ws.add(obj);

ws.has(window); // true
ws.has(foo);    // false

ws.delete(window);
ws.has(window);    // false

WeakSet 的成员只能是对象,而不能是其他类型的值

let weak = new WeakSet();
weak.add(1);//Uncaught TypeError: Invalid value used in weak

正如它的名字weak,它是对象的弱引用,垃圾回收机制不考虑,所以不会造成内存的泄露,同时是无法遍历的,因为你不知道什么时候对象的引用消失了,而且没有size属性,不知道长度,可以试试以下代码在浏览器的输出

let weak = new WeakSet();     
weak.add({});
weak.add([]);
let timeId = setInterval(() => {
    console.log(weak);
}, 1000);

在这里插入图片描述

19、Map

Map是一种数据结构

let m = new Map() //创建实例
m.set("name","tom") //存储值
console.log(m.get("name"))//获取name
console.log(m.has('name'))//是否有name键
m.clear()//清空整个map
m.forEach((value,key)=>{  //遍历
    console.log(key+":"+value)
})

可迭代

19、WeakMap

弱映射,是一种增强的键值对存储机制。(weak描述的是JavaScript垃圾回收程序对待“若映射”中键的方式)

因为没有指向这个对象的其他引用,所以当这行代码执行完成后,这个对象就好被当做垃圾回收。然后这个键值对就从弱映射中消失了,然后形成一个空映射

 const wm = new WeakMap()
 const container = {
   key : {}
 }
 wm.set(container.key,"val")
function removeReference(){
  container.key = null
}

使用场景:访问计数器
在这里插入图片描述
需要注意的地方:

(1)不可迭代枚举,因为我们不知道何时垃圾回收器会移除这个对象。

(2)weakmap对象是不可枚举的,无法获取大小

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/WeakMap

20、Symbol

  1. Symbol不是一个构造函数,不能使用new来创建
const symbol = Symbol()
console.log(symbol) //Symbol()
  1. Symbol可以传递一个字符串参数,参数作用是对symbol类型的描述,便于区分这个symbol是哪一个,
const s1 = Symbol('foo')
const s2 = Symbol('bar')
console.log(s1,s2)   // Symbol(foo) Symbol(bar)
  1. Symbol类型的值具有唯一性,是一个独一无二的值,每一个 Symbol 的值都不相等。相同参数 Symbol() 返回的值不相等
const s1 = Symbol('foo')
const s2 = Symbol('foo')
console.log(s1===s2) // false
  1. Symbol私有变量
const name = Symbol()
const obj = {
    [name]:'123',
    sayHi(){
        console.log(this[name])
    }
}
console.log(obj.name) //不能直接访问
obj.sayHi() //只能调用其它非私有的方法
  1. for in 获取不到symbol值
for(let key in obj){
   console.log(key)
}   //sayHi
  1. keys也获取不到值
console.log(Object.keys(obj))   // ['sayHi']
  1. 补充
    复用symbol值,因为symbol产生的值不是唯一的,这里提供了一个symbol.for方法
const s1 = Symbol.for('foo')
const s2 = Symbol.for('foo')
console.log(s1 === s2) // true
// 会把字符串类型的值 转换成 boolean类型值
console.log(Symbol.for(true) === Symbol.for('true'))

// 如果我们想自定义标签[object object]

//const obj = {}
//console.log(obj.toString()) //[object object]

// 如果我们想自定义标签

const obj = {
	[Symbol.toStringTag]: 'XObject'
}
console.log(obj.toString()) //[object XObject]

通过getOwnPropertySymbols获取symbol属性名

const obj = {
	[Symbol('str')]: 'symbol value',
	foo: 'normal value'
}

console.log(Object.getOwnPropertySymbols(obj))
//[ Symbol('str') ]

获取Symbol属性方法

console.log(Object.getOwnPropertySymbols(obj))

21、for…of循环

(1).遍历数组 和forEach对比

//数组遍历
const arr = [1,2,3,4,5]
for (const item of arr) {
    console.log(item) // 1,2,3,4,5
    if(item > 2{
      break; // 可以终止循环
    }
}

arr.forEach() // 不能跳出循环

(2)遍历set集合

const set = new Set([1,2,3,4,5])
for (const item of set) {
    console.log(item)//1,2,3,4,5
}

(3)遍历Map集合

let map = new Map()
map.set("name","Tom")
map.set("age",12)
for (const [key,value] of map) {
    console.log(key+":"+value)
}

(4)for of 内部实现原理 数组集合原型对象上都有Symbol.iterator方法,调用iterator方法得到一个迭代器,从而遍历内部所有的数据。所以只有实现了Iterator接口才可以进行for…of的遍历

const arr = [1,2,3]
const iterator = arr[Symbol.iterator]()
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())

(5) ES2015可迭代接口实现

const obj = {
	store: ['foo', 'bar', 'baz'],
	[Symbol.iterator]: function() {
		let index = 0
		const self = this
		return {
			next: function() {
				const result = {
					value: self.store[index],
					done: index >= self.store.length
				}
				index++
				return result
			}
		}
	}
}
for (const item of obj) {
	console.log(item) // foo、bar、baz
}

(6) 用下面例子 看看可迭代接口作用
现在有一个对象

const todos = {
  life: ['吃饭', '睡觉', '打豆豆'],
  learn: ['语文', '数学', '外语'],
  work: ['喝茶'],
 } 

我们想遍历对象里面 生活类、学习类内容,通常会这样,逐个数组遍历

   for (const item of todos.life) {
     console.log(item)
   }
   for (const item of todos.learn) {
     console.log(item)
   }
   for (const item of todos.work) {
     console.log(item)
   }

我们也可以提供统一遍历访问接口

const todos = {
  life: ['吃饭', '睡觉', '打豆豆'],
  learn: ['语文', '数学', '外语'],
  work: ['喝茶'],

  // 提供统一遍历访问接口
  each: function (callback) {
    const all = [].concat(this.life, this.learn, this.work)
    for (const item of all) {
      callback(item)
    }
  },
}  

todos.each(function (item) {
  console.log(item)
})

提供迭代器(ES2015 统一遍历访问接口)

// 迭代器设计模式

// 场景:你我协同开发一个任务清单应用

// 我的代码 ===============================

const todos = {
  life: ['吃饭', '睡觉', '打豆豆'],
  learn: ['语文', '数学', '外语'],
  work: ['喝茶'],

  // 提供迭代器(ES2015 统一遍历访问接口)
  [Symbol.iterator]: function () {
    const all = [...this.life, ...this.learn, ...this.work]
    let index = 0
    return {
      next: function () {
        return {
          value: all[index],
          done: index++ >= all.length
        }
      }
    }
  }
}

for (const item of todos) {
  console.log(item)
}

这样你不用关心对象的内部结构 是怎样的!对外提供统一接口

22、生成器函数

function * foo () {
  console.log('1111')
  yield 100
  console.log('2222')
  yield 200
  console.log('3333')
  yield 300
}

const generator = foo()

console.log(generator.next()) // 第一次调用,函数体开始执行,遇到第一个 yield 暂停
console.log(generator.next()) // 第二次调用,从暂停位置继续,直到遇到下一个 yield 再次暂停
console.log(generator.next()) // 。。。
console.log(generator.next()) // 第四次调用,已经没有需要执行的内容了,所以直接得到 undefined

用生成器函数实现iterator方法

const todos = {
  life: ['吃饭', '睡觉', '打豆豆'],
  learn: ['语文', '数学', '外语'],
  work: ['喝茶'],
  [Symbol.iterator]: function * () {
    const all = [...this.life, ...this.learn, ...this.work]
    for (const item of all) {
      yield item
    }
  }
}

for (const item of todos) {
  console.log(item)
}

22、迭代器和异步迭代器

什么是同步迭代器呢?
例子:

var obj = {
  *[Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3;
  }
}

var iterator = obj[Symbol.iterator]()

iterator.next() // {value: 1, done: false}
iterator.next() // {value: 2, done: false}
iterator.next() // {value: 3, done: false}
iterator.next() // {value: undefined, done: true}

这里的 iterator 就是同步迭代器了,每调用一次 next 方法,就返回一个 { value: xx, done: xx } 形式的对象。

什么是异步迭代器呢?
再举个例子:

var obj = {
  async *[Symbol.asyncIterator]() {
    yield 1;
    yield 2;
    yield 3;
  }
}

var asyncIterator = obj[Symbol.asyncIterator]()

asyncIterator.next().then(data => console.log(data)) // {value: 1, done: false}
asyncIterator.next().then(data => console.log(data)) // {value: 2, done: false}
asyncIterator.next().then(data => console.log(data)) // {value: 3, done: false}
asyncIterator.next().then(data => console.log(data)) // {value: undefined, done: true}

同步迭代器数据如果即用即给,处理顺序等于遍历顺序。

var obj = {
  *[Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3;
  }
}

for (let item of obj) {

console.log(item) // 1 -> 2 -> 3。处理顺序等于遍历顺序
}

再来看看异步迭代器

var obj = {
  async *[Symbol.asyncIterator]() {
    yield new Promise(resolve => setTimeout(() => resolve(1), 5000));
    yield new Promise(resolve => setTimeout(() => resolve(2), 3000));
    yield new Promise(resolve => setTimeout(() => resolve(3), 500));
  }
}

console.log(Date.now())
for await (let item of obj) {

console.log(Date.now(), item)
}

// 1579256590699
// 1579256595700 1 // 1579256595700 - 1579256590699 = 5001
// 1579256598702 2 // 1579256598702 - 1579256590699 = 8003
// 1579256599203 3 // 1579256599203 - 1579256590699 = 8504

for-await-of 除了能用在异步可迭代对象上,还能用在同步可迭代对象上。

var obj = {
  *[Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3;
  }
}

for await(let item of obj) {

console.log(item) // 1 -> 2 -> 3
}

22、生成器和迭代器区别

JavaScript中的生成器(Generator)和迭代器(Iterator)是两种不同的概念和用法。

生成器是一种特殊的函数,可以通过yield语句来暂停函数的执行,并且可以通过next()方法来恢复函数的执行。生成器函数可以返回一个可迭代对象,该对象可以用于循环遍历生成器函数中的yield语句返回的值。生成器函数可以用于惰性地生成一系列值,而不需要一次性生成所有值。生成器可以在需要的时候逐步生成值,这样可以节省内存和提高性能。

迭代器是一种对象,它定义了一个next()方法,用于返回序列中的下一个值。迭代器可以用于遍历一个序列,例如数组或者字符串。当调用next()方法时,迭代器会返回一个包含value和done属性的对象。value属性表示序列中的下一个值,done属性表示是否已经遍历完序列。迭代器可以用于实现自定义的可迭代对象,使其可以通过for…of循环来遍历。

总结来说,生成器是一种特殊的函数,它可以通过yield语句来暂停和恢复函数的执行,并且可以生成一个可迭代对象。而迭代器是一种对象,它定义了一个next()方法,用于遍历一个序列中的值。生成器可以用于惰性地生成一系列值,而迭代器可以用于遍历一个序列。

23、indexOf和includes方法的区别

indexOf方法查找不到NAN这样的数值,includes可以

   const arr = ['foo', 1, NaN, false]
// 找到返回元素下标
   console.log(arr.indexOf('foo')) // 1
// 找不到返回 -1
   console.log(arr.indexOf('bar')) // -1
// 无法找到数组中的 NaN
   console.log(arr.indexOf(NaN)) // -1 无法找到NAN
// 直接返回是否存在指定元素
   console.log(arr.includes('foo')) // true
// 能够查找 NaN
   console.log(arr.includes(NaN)) // true

24、指数运算符

// console.log(Math.pow(2, 10))
console.log(2 ** 10)

25、ECMAScript 2017

(1)Object.values(),返回一个对象所有value的数组

const obj = {
    name:'tom',
    age:12
}
console.log(Object.values(obj) // [ 'tom', 12 ]


(2)Object.entries(),返回属性值对应的数组

   console.log(Object.entries(obj))  //  [ [ 'name', 'tom' ], [ 'age', 12 ] ]

   for (const [key, value] of Object.entries(obj)) {
     console.log(key, value)
   }
   const books = {
     html: 5,
     css: 16,
     javascript: 128
   }

      for (const [name, count] of Object.entries(books)) {
        console.log(name, count)
      }
//html 5
//css 16
//javascript 128
   for (const [name, count] of Object.entries(books)) {
     console.log(`${name.padEnd(16, '-')}|${count.toString().padStart(3, '0')}`)
    }
//html------------|005
//css-------------|016
//javascript------|128

(3)Object.assign()的弊端,只能复制属性值,方法不能被赋值,通过输出值可以看出,输出的fullName不是一个函数,而是一个固定的字符串

const p = {
    firstName:'Lei',
    lastName : 'Wang',
    get fullName(){
        return this.firstName+" "+this.lastName
    }
}

console.log(p.fullName) // Lei Wang
let p2 = {}
Object.assign(p2,p)
console.log(p2) // { firstName: 'Lei', lastName: 'Wang', fullName: 'Lei Wang' }


(4)使用 Object.getOwnPropertyDescriptors与Object.defineProperties

const descriptor = Object.getOwnPropertyDescriptors(p)
console.log(descriptor)
const copyP = Object.defineProperties({},descriptor)
console.log(copyP)  // { firstName: 'Lei', lastName: 'Wang', fullName: [Getter] }
copyP.firstName = 'qiuqiu'
console.log(copyP.fullName)

26、for await of

异步迭代器(for-await-of):循环等待每个Promise对象变为resolved状态才进入下一步。

我们知道 for…of 是同步运行的,看如下代码

  function TimeOut(time) {
   return new Promise(function (resolve, reject) {
    setTimeout(function () {
     resolve(time)
    }, time)
   })
  }
  async function test() {
   let arr = [TimeOut(2000), TimeOut(1000), TimeOut(3000)]
   for (let item of arr) {
    console.log(Date.now(), item.then(console.log))
   }
  }
  test() 

上面打印结果如下图
在这里插入图片描述
上述代码证实了 for of 方法不能遍历异步迭代器,得到的结果并不是我们所期待的,于是 for await of 就粉墨登场啦!

ES9 中可以用 for…await…of 的语法来操作

 function TimeOut(time) {
   return new Promise(function (resolve, reject) {
    setTimeout(function () {
     resolve(time)
    }, time)
   })
  }
  async function test() {
   let arr = [TimeOut(2000), TimeOut(1000), TimeOut(3000)]
   for await (let item of arr) {
    console.log(Date.now(), item)
   }
  }
  test() // 1560092345730 2000// 1560092345730 1000// 1560092346336 3000复制代码

for await of 环等待每个Promise对象变为resolved状态才进入下一步。所有打印的结果为 2000,1000,3000

27、flat()

ES10

新增了flat()方法用于将嵌套的数组扁平化,并且还引入了flatMap()方法,它结合了map()和flat(),可以一次性对数组进行映射和扁平化操作。

按照一个可指定的深度递归遍历数组:
默认扁平化第一层

const arr1 = [0, 1, 2, [3, 4]];
document.getElementById("demo1").innerHTML = arr1.flat();
 
const arr2 = [0, 1, 2, [[[3, 4]]]];
document.getElementById("demo2").innerHTML = arr2.flat(2);

28、BigInt

ES2011
BigInt:引入了新的基本数据类型BigInt,用于表示任意精度的整数。BigInt可以表示比JavaScript中Number类型范围更大的整数,以及更高的精度。

29、Promise.allSettled()

ES11

首先通过 promise 对象实现 Ajax (如下) ,后面 在代码中 会使用到

 const getJSON = function(url) {
    const promise = new Promise(function(resolve, reject){
      const handler = function() {
        if (this.readyState !== 4) {
          return;
        }
        if (this.status === 200) {
          resolve(this.response);
        } else {
          reject(new Error(this.statusText));
        }
      };
      const client = new XMLHttpRequest();
      client.open("GET", url);
      client.onreadystatechange = handler;
      client.responseType = "text";
      client.setRequestHeader("Accept", "application/json");
      client.send();
    });
    return promise;
  };

Promise.allSettled()

有时候,我们希望等到一组异步操作都结束了,不管每一个操作是成功还是失败,再进行下一步操作。但是,现有的 Promise 方法很难实现这个要求。
Promise.all() 方法只适合所有异步操作都成功的情况,如果有一个操作失败,就无法满足要求。 Promise.all()方法

const urls = [url_1, url_2, url_3];
const requests = urls.map(x => fetch(x));
 
try {
  await Promise.all(requests);
  console.log('所有请求都成功。');
} catch {
  console.log('至少一个请求失败,其他请求可能还没结束。');
}

上面示例中,Promise.all()可以确定所有请求都成功了,但是只要有一个请求失败,它就会报错,而不管另外的请求是否结束。

为了解决这个问题,ES2020 引入了 Promise.allSettled()方法,用来确定一组异步操作是否都结束了(不管成功或失败)。所以,它的名字叫做”Settled“,包含了 ”fulfilled“和 ”rejected“两种情况。

Promise.allSettled()方法接受一个数组作为参数,数组的每个成员都是一个 Promise 对象,并返回一个新的 Promise 对象。只有等到参数数组的所有 Promise 对象都发生状态变更(不管是fulfilled还是 rejected),返回的 Promise 对象才会发生状态变更。

  const promises = [
    getJSON('https://jsonplaceholder.typicode.com/users/1'),
    getJSON('https://jsonplaceholder.typicode.com/uses/2'),
    getJSON('https://jsonplaceholder.typicode.com/users/3'),
 ];
  
 
   await Promise.allSettled(promises);
     removeLoadingIndicator();

上面示例中,数组 promises包含了三个请求,只有等到这三个请求都结束了

(不管请求成功还是失败),removeLoadingIndicator()才会执行。

在这里插入图片描述
该方法返回的新的 Promise 实例,一旦发生状态变更,状态总是 fulfilled,不会变成rejected。状态变成 fulfilled后,它的回调函数会接收到一个数组作为参数,该数组的每个成员对应前面数组的每个 Promise 对象。

const resolved = Promise.resolve(42);
const rejected = Promise.reject(-1);
 
const allSettledPromise = Promise.allSettled([resolved, rejected]);
 
allSettledPromise.then(function (results) {
  console.log(results);
});
// [
//    { status: 'fulfilled', value: 42 },
//    { status: 'rejected', reason: -1 }
// ]

上面代码中,Promise.allSettled()的返回值 allSettledPromise,状态只可能变成 fulfilled。它的回调函数接收到的参数是数组 results。该数组的每个成员都是一个对象,对应传入 Promise.allSettled()的数组里面的两个 Promise 对象。

results的每个成员是一个对象,对象的格式是固定的,对应异步操作的结果。

// 异步操作成功时
{status: 'fulfilled', value: value}
 
// 异步操作失败时
{status: 'rejected', reason: reason}

成员对象的 status属性的值只可能是字符串 fulfilled或字符串 rejected,用来区分异步操作是成功还是失败。如果是成功(fulfilled),对象会有 value属性,如果是失败(rejected),会有 reason属性,对应两种状态时前面异步操作的返回值。

下面是返回值的用法例子。

const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
const results = await Promise.allSettled(promises);
 
// 过滤出成功的请求
const successfulPromises = results.filter(p => p.status === 'fulfilled');
 
// 过滤出失败的请求,并输出原因
const errors = results
  .filter(p => p.status === 'rejected')
  .map(p => p.reason);

30、Promise.any()

Promise.any():新增了Promise.any()方法,用于接收一个Promise数组,并在其中至少有一个Promise成功解决时返回一个新的Promise。与Promise.race()不同,Promise.any()会忽略失败的Promise,只要有一个成功即可。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值