ES6新特性总结

let

1、不能被重复定义

let a
let a
//报错:Uncaught SyntaxError: Identifier 'a' has already been declared

2、let不存在变量提升,而var存在

var

console.log(a) //undefined
var a=1
console.log(a) //1

var在块级作用域中,会将var的声明提前至前面,源代码中,其实真实的代码应该是

var a
console.log(a) //undefined
a=1
console.log(a) //1

let

console.log(a) 
//报错:Uncaught ReferenceError: Cannot access 'a' before initialization
let a=1
console.log(a) 

3、let拥有块级作用域

let命令声明的变量只在其块级作用域中有效,就是{}中,在块级作用域内,若存在用let命令声明的变量,则所在区块对该变量形成封闭作用域,即该变量无视外部的同名变量。

let a=2
{
    let a=1
    console.log(a) //1
}

还有一个网上常说的例子

let items = document.querySelectorAll('.item');

for (var i = 0; i < items.length; i++) {
  items[i].onclick = function () {
    items[i].style.background = 'blue';
  }
}

当我们点击第一,第二,或者第三个块时,类型错误,就是没有找到 item[i] 这个变量,这是由于使用 var 定义的变量。

在for循环中这个块中使用var声明的变量,默认就是全局变量,在 window 上,可以直接访问到。而在for循环这个块中访问到的是全局的变量 i , i++ 在执行完之后,此时在 window.i = 3

这时就用到了,let 声明变量,就会在for循环这个块中,产生一个块级作用域,与全局作用域无关,items[i] 就是我们想要的变量。


const

1、const 声明一个只读的常量。一旦声明,常量的值就不能改变。正是因为其值不能进行修改,因此, ,const一旦声明变量,就必须立即初始化,不能留到以后赋值,const定义的变量潜规则要大写。

2、const与let一样, 只在声明所在的块级作用域内有效

3、const命令声明的常量不存在提升

4、const命令声明的常量同样存在暂时性死区,只能在声明的位置后面使用。

5、const声明的常量,也与let一样不可重复声明

本质:const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。

简单数据类型( 数值、字符串、布尔值): 值就保存在变量指向的那个内存地址,因此等同于常量

复合数据类型( 对象和数组) : 变量指向的内存地址,保存的只是一个指向实际数据的指针 , const只能保证这个指针是固定的 至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。


箭头函数

一个简单的改写案例

let add1 = function(a,b){
    return a+b
}
let add2 = (a,b)=>{
    return a+b
}
console.log(add1(1,2)); //3
console.log(add2(2,3)); //5

与function的区别

1、this的指向问题

箭头函数中,this是静态的,始终指向函数声明时,所在作用域下this的值,所以箭头函数适合与 this 无关的回调

function getname(){
    console.log(this.name);
}
getname2 = ()=>{
    console.log(this.name);
}

//设置 window 对象的 name 属性
window.name = 'window'

//设置一个对象中的 name 属性
const obj = {
    name:'object'
}

//直接调用
getname()  //window
getname2() //window

//使用 call 方法改变
//call 方法,可以可以编写能够在不同对象上使用的方法,用来调用所有者对象作为参数的方法
getname.call(obj) //object
getname2.call(obj) //window

2、不能够作为构造化实例对象

let person = (name,age)=>{
    this.name = name
    this.age = age
}

let p = new person('pzw',20)

//报错:Uncaught TypeError: person is not a constructor

很好理解,因为箭头函数的this指向始终是静态的,所以无法成为一个构造器

3、无法使用argumens 但是可以使用…rest

let fn = ()=>{
    console.log(arguments)
}

fn(1,2,3)

//报错:Uncaught ReferenceError: arguments is not defined

let fn = (...rest)=>{
    console.log(rest)
}

fn(1,2,3) //[1,2,3]

4、箭头函数的简写

  1. 当参数只有一个的时候,左边的括号可以不写
  2. 当右边只有一条语句时,函数花括号可以不写,如果函数仅仅语句只有 return 的话,那么 return 必须省略,函数的执行结果就是 return 的值
let pow = n => n*n
console.log(pow(3)) //9

扩展运算符

扩展运算符( spread )是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,例如:

console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
[...document.querySelectorAll('div')]
// [
,
,
] function push(array, ...items) { array.push(...items); } function add(x, y) { return x + y; } var numbers = [4, 38]; add(...numbers) // 42

替代数组的 apply 方法

// ES5 的写法
Math.max.apply(null, [14, 3, 77])
// ES6 的写法
Math.max(...[14, 3, 77])
//  等同于
Math.max(14, 3, 77);

解构赋值

灵活的数组解构赋值

let arr = [1,2,3,4,5,6]
let va = arr[4]
let [,a,b,c,...rest] = arr
console.log(a,b,c,rest) //2,3,4,[5,6]

对象的解构赋值

let p1 = {
    "name":"zhuangzhuang",
    "age":25
}
let {name,age} = p1;//注意变量必须为属性名
console.log(name,age);//"zhuangzhuang",25

在react中会经常用到

const { cur } = this.state

模板字符串

键盘Tab上的键 ``
ES6 引入新的声明字符串的方式 , 对比 ‘’, “” 的优点:

1、内容可直接换行,不用再使用 ‘+’ 拼接
2、可以直接拼接变量

let white = '小白';
let cat = `
  • 你好,${white}
`
; console.log(cat); 直接输出: <ul> <li>你好,${white}</li> </ul>

rest参数

rest 参数,用于获取函数的实参,代替 arguments
当我们使用 arguments 时:

function date() {
    console.log(arguments);
    console.log(typeof arguments);
}
date(1, 2, 3, 4);

ES6新特性总结_第1张图片
可以看到 arguments 原型中 constructor 属性为 Object,这就说明了 arguments 并不是数组,因此不能使用数组的方法,因此在处理多个参数时,并不能使用更加方便的数组方法进行处理。

接下来,我们再看一下 rest 获取参数的类型:

function date(...rest) {
    console.log(rest);
    console.log(typeof rest);
}
date(1, 2, 3, 4);
//打印[1, 2, 3, 4]

迭代器(Iterator)

首先说说for in,for of
简单来说

  • for in遍历的是数组的索引(即键名),而for of遍历的是数组元素值。
  • for-in总是得到对象的key或数组、字符串的下标。
  • for-of总是得到对象的value或数组、字符串的值,另外还可以用于遍历Map和Set。
const arr = ['red', 'black', 'blue']

for(let i of arr) {
	console.log(i)  // 'red' 'black' 'blue'
}

for(let i in arr) {
    console.log(i)  // 1, 2, 3
}

如何使用 for…of 遍历自定义的数组呢,比如对象中不包含 迭代器(Iterator),但是可以自定义一个迭代器。

// 直接使用 for...of 遍历对象
const obj = {
  arr: ['red', 'blue', 'green']
}

for(let i of obj) {
  console.log(i) // Uncaught TypeError: obj is not iterable
}

// 给对象添加一个迭代器
const obj = {
  color: ['red', 'blue', 'green'],

  [Symbol.iterator]() {
    let index = 0
    return {
      next: () => {
        if (index < this.color.length) {
          const res = { value: this.color[index], done: false }
          index++
          return res
        }else {
          return { value: undefined, done: true }
        }
      }
    }
  }
}

for (let i of obj) {
  console.log(i)  // 'red' 'blue', 'green'
}

生成器(generator)

在 ES6 中定义一个生成器函数很简单,在 function 后跟上「*」即可:

function* myGenerator() {
  yield 'hello';
  yield 'world';
  return 'Generator';
}

var g = myGenerator();

g.next(); // { value: 'hello', done: false }
g.next(); // { value: 'world', done: false }
g.next(); // { value: 'ending', done: true }
g.next(); // { value: undefined, done: true }

调用生成器函数会产生一个生成器(generator)。生成器拥有的最重要的方法是 next(),用来迭代。

yield 主要用作代码隔断,生成器(generator)在执行的时候必须使用生成器(iterator)的next() 迭代。一个 next() 只能迭代一段 yield 代码。

我所理解的yield,就是程序执行的一个断点,用来暂停和继续一个生成器函数。

最后一次调用,Generator函数已经运行完毕,next方法返回对象的value属性为undefined,done属性为true

实现斐波那契数列例子

function* fab(max) {
    var count = 0, last = 0, current = 1;

    while(max > count++) {
        yield current;
        var tmp = current;
        current += last;
        last = tmp;
    }
}

var o = fab(10), ret, result = [];

while(!(ret = o.next()).done) {
    result.push(ret.value);
}

console.log(result); // [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

Symbol 新增数据类型

ES6 新引入的基础数据类型,表示独一无二的值,是一种类似于字符串的类型。

第七种基本数据类型(Undefined、Null、Boolean、Number,String,Object,Symbol )

Symbol 特点

值是唯一的,用来解决命名冲突

  • 不能与其他数据类型进行运算
  • 定义的对象属性,不能使用 for···in 遍历,但是可以用Reflect.ownKeys 来获取对象的所有键名

简单实例

const s1 = Symbol('1')
// 或者:
const s2 = Symbol.for('1')

// 给对象添加 Symbol 类型的属性,保护对象属性的安全性
const obj = {
	name: 'xiaobai',
    [Symbol('say')]: function() {
        console.log('说话')
    }
}
obj.[Symbol('say')]()

// 或者
const obj1 = {
	up: '',
    dowm: ''
}
const methods = {
    up: Symbol(),
    dowm: Symbol()
}
obj[methods.up] = function() {
	
}
obj[methods.down] = function() {
	
}
// 这样创建的 up 和 dowm 方法是唯一的,不会和obj1中的up,dowm 属性发生冲突,保护了属性的安全。


Promise

在开始使用Promise之前,我们首先需要了解Promise的三种状态:

  • pending: 初始状态,既不是成功,也不是失败状态。
  • fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失败。

pending 状态的 Promise 对象可能会变为fulfilled 状态并传递一个值给相应的状态处理方法,也可能变为失败状态(rejected)并传递失败信息。当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers )就会被调用(then方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型。当Promise状态为fulfilled时,调用 then 的 onfulfilled 方法,当Promise状态为rejected时,调用 then 的 onrejected 方法, 所以在异步操作的完成和绑定处理方法之间不存在竞争)。

因为 Promise.prototype.thenPromise.prototype.catch 方法返回promise 对象, 所以它们可以被链式调用。

ES6新特性总结_第2张图片

一个Promise的基本案例

let p = new Promise((resolve,reject)=>{
    setTimeout(() => {

        let data = '数据'
        resolve(data)

        // let err = '数据读取错误'
        // reject(err)

    }, 1000)
}).then((data)=>{
    console.log(data);
},(err)=>{
    console.log(err);
})

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。

Resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;

Reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

Promise 封装读取文件

var fs = require('fs')

let readfile = new Promise((res,rej)=>{
    fs.readFile('./file.md','utf-8',(err,data)=>{
        if (err) rej(err)
        res(data)
    })
}).then((data)=>{
    console.log(data.toString());
},(err)=>{
    console.log('读取失败');
})

Promise封装 Ajax请求

let getajax = new Promise((res,rej)=>{
    const xhr = new XMLHttpRequest()
    xhr.open('GET','https://www.baidu.com')
    xhr.send()
    xhr.onreadystatechange = ()=>{
        if (xhr.readyState === 4){
            if (xhr.state >= 200 && xhr.state <= 299){
                res(xhr.response)
            }else{
                rej(xhr.status)
            }
        }
    }
}).then(data=>{
    console.log(data);
},err=>{
    console.error(err);
})

catch语法糖

getajax.catch(err =>{
    console.log(err)
})

Set集合

ES6 新的数据结构 Set(集合),虽然类似于数组,但是成员的每一个值都是唯一的。集合实现了迭代器(iterator)接口,所以可以使用扩展运算符(…)和for…of进行遍历(让我想到C++的STL的库= 。=)。

集合的属性和方法:

  • size:返回集合的元素个数
  • add:增加一个新元素,返回当前集合
  • delete:删除元素,返回一个Boolean值
  • has:检测集合中是否包含某个元素,返回Boolean值
  • clear:清空集合
const set = new Set(['red', 'blue', 'green'])

const res1 = set.has('red')
console.log(res1)  // true

const r2 = set.size
console.log(r2)  // 3

set.add('white')
console.log(set) // Set(4) {"red", "blue", "green", "white"}

set.delete('red')
console.log(set) // Set(3) {"blue", "green", "white"}

set.clear()
console.log(set) // Set(0) {}

Set集合的应用:

数组去重

const arr1 = [1, 2, 3, 3, 4, 2, 5]
const res1 = [...new Set(arr1)]
console.log(res1)  // [1, 2, 3, 4, 5]

两个数组取交集

const arr1 = [1, 2, 3, 3, 4, 2, 5]
const arr2 = [4, 5, 6, 7, 5, 6]

const res2 = [...new Set(arr1)].filter(item => {
  const s = new Set(arr2)
  if(s.has(item)) {
    return true
  }else {
    return false
  }
})
console.log(res2) // [4, 5]
// 或者
const res3 = [...new Set(arr1)].filter(item => new Set(arr2).has(item))
console.log(res3) // [4, 5]

两个数组并集(数组合并)

const arr1 = [1, 2, 3, 3, 4, 2, 5]
const arr2 = [4, 5, 6, 7, 5, 6]
const res4 = [...new Set(arr1), ...new Set(arr2)]
console.log(res4) // [1, 2, 3, 4, 5, 4, 5, 6, 7]

数组差集

const arr1 = [1, 2, 3, 3, 4, 2, 5]
const arr2 = [4, 5, 6, 7, 5, 6]
const res5 = [...new Set(arr1)].filter(item => !new Set(arr2).has(item))
console.log(res5) // [1, 2, 3]


Map

ES6 新的数据结构。类似于对象,键值对的集合。但是‘键’的范围不限于字符串。各种类型的值(包括对象)都可以当做键。Map 也实现了 iterator 接口,可以使用扩展运算符(…)和for…of遍历(又是一个STL类型)

Map 的属性和结构

  • size:返回Map元素的个数
  • set:增加一个新元素,返回当前 Map
  • get:返回键名对象的键值
  • has:检测 Map 中是否含某个元素,返回Boolean值
  • clear:清空集合

使用

const map = new Map()

map.set('name', 'zhangsan')
map.set('age', 18)
map.set('say', function() {
  console.log('hello')
})

console.log(map)  // {"name" => "zhangsan", "age" => 18, "say" => ƒ}

console.log(map.size) // 3

const res1 = map.get('name')
console.log(res1)  // '张三'

const res2 = map.has('age')
console.log(res2)  // true

const res3 = map.delete('say')
console.log(res3)  // true


class

概述

ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用ES6的“类”改写,就是下面这样

class stu{
    static school = 'HZNU' //还拥有static 静态属性
    //静态属性指的是 Class 本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性
    constructor(name,age){
        this.name = name
        this.age = age
    }
    print(){
        console.log('我的名字是'+this.name+',我的年龄是'+this.age+',我在'+stu.school+'上学');
    }
}

let stu1 = new stu('pzw',21)
stu1.print() //我的名字是pzw,我的年龄是21,我在HZNU上学

类的继承

class stu13 extends stu{
    static school = 'GDZX'
    static banji = 13
    constructor(name,age,bzr){
        super(name,age)
        this.bzr = bzr
    }
    print13(){
        console.log('我的名字是'+this.name+',我的年龄是'+this.age+',我在'+stu13.school+'上学,班级是'+stu13.banji+'班,我的班主任是'+this.bzr);
    }
}

let stu2 = new stu13('hhb',21,'ZXY')
stu2.print13() //我的名字是hhb,我的年龄是21,我在GDZX上学,班级是13班,我的班主任是ZXY

get set

在类里面可以去定义一些getter和setter,getter可以得到一些东西的方法,setter可以设置东西

class classP{
    constructor(){
        this.arr = []
    }
    get queue(){
        return this.arr
    }
    set queue(name){
        this.arr.push(name)
    }
}

let class13 = new classP()
class13.queue = 'pzw'
class13.queue = 'hhb'
console.log(class13.queue) //["pzw", "hhb"]

对象(Object)方法的扩展

Object.is

判断两个值是否相等,相当于全等于(===),但是NaN 和 (-0, 0)判断结果两者不相等

console.log(Object.is(123, 123))  // true
console.log(Object.is('red', 'red'))  // true
console.log(Object.is(NaN, NaN))  // true  ===结果为false
console.log(Object.is(-0, 0))  // false  ===结果为true

下图所示,展示了使用==,===和Object.is的结果差异:
ES6新特性总结_第3张图片

Object的遍历方法

const obj = {
    book: "Learning ES2017 (ES8)",
    author: "前端达人",
    publisher: "前端达人",
    useful: true
};
console.log(Object.keys(obj)) // [ 'book', 'author', 'publisher', 'useful' ]
console.log(Object.values(obj)) //[ 'Learning ES2017 (ES8)', '前端达人', '前端达人', true ]
console.log(Object.entries(obj)) 
/*
[
  [ 'book', 'Learning ES2017 (ES8)' ],
  [ 'author', '前端达人' ],
  [ 'publisher', '前端达人' ],
  [ 'useful', true ]
]*/
//Object.fromEntries()方法是Object.entries()的逆操作,用于将一个键值对数组转为对象。
const entries = new Map([
    ['foo', 'bar'],
    ['baz', 42]
]);
Object.fromEntries(entries) // { foo: "bar", baz: 42 }  

Object.assign

对象合并,后一个对象中的属性值会覆盖前一个对象上的属性值,如果前一个对象中不存在后一个对象中的某个属性,就会在对象中添加这个属性。

const data1 = {
  name: 'zhangsan',
  age: 15,
  color: 'red'
}

const data2 = {
  name: 'zhangsan',
  age: 15,
  color: 'blue',
  height: 60
}

const data = Object.assign(data1, data2)
console.log(data)  // {name: "zhangsan", age: 15, color: "blue", height: 60}

数组(Array)方法扩展

includes

includes 方法用来检测数组中是否包含某个元素,返回 Boolean 值

const arr = ['red', 'blue', 'green', 'white']
console.log(arr.includes('red')) // true
console.log(arr.includes('black')) // false

flat 和 flatMap

const arr1 = [1, 2, 3, [4, 5, 6, [7, 8]]]
console.log(arr1.flat())  // [1, 2, 3, 4, 5, 6, [7, 8]]
// 传参表示深度
console.log(arr1.flat(2)) // // [1, 2, 3, 4, 5, 6, 7, 8]

map

map()方法定义在JavaScript的Array中,它返回一个新的数组,数组中的元素为原始数组调用函数处理后的值。

let numbers = [4, 9, 16, 25]
let sq = numbers.map(value=>{
    return Math.sqrt(value)
})
console.log(sq); //[ 2, 3, 4, 5 ]

map会对数组的每一项进行处理,返回新数组,返回的新数组包含对之前每一项处理结果;

filter

filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。

let numbers = [4, 9, 16, 25]
let sq = numbers.filter(value=>{
    return value === 16
})
console.log(sq)  //[ 16 ]

every

依据判断条件,数组的元素是否全满足,若满足则返回true

let arr = [1,2,3,4,5]
let arr1 = arr.every( (value, index) =>value<3)
console.log(arr1) // false
let arr2 = arr.every( (value, index) =>value<6)
console.log(arr2)  // true

reduce

arr.reduce(callback, initialValue) 迭代数组的所有项,累加器,数组中的每个值(从左到右)合并,最终计算为一个值
 参数: callback: previousValue 必选 --上一次调用回调返回的值
         currentValue 必选 --数组中当前被处理的数组项
         index 可选 --当前数组项在数组中的索引值
         array 可选 --原数组
     initialValue: 可选 --初始值

let arr = [0,1,2,3,4]
let arr1 = arr.reduce((preValue, curValue) => 
    preValue + curValue
)
console.log(arr1)    // 10

reduce的高级用法
(1)计算数组中每个元素出现的次数

let names = ['peter', 'tom', 'mary', 'bob', 'tom','peter'];
let nameNum = names.reduce((pre,cur)=>{
  if(cur in pre){
    pre[cur]++
  }else{
    pre[cur] = 1
  }
  return pre
},{})
console.log(nameNum); //{ peter: 2, tom: 2, mary: 1, bob: 1 }

(2)数组去重

let arr = [1,2,3,4,4,1]
let newArr = arr.reduce((pre,cur)=>{
    if(!pre.includes(cur)){
        pre.push(cur)
    }
    return pre
},[])
console.log(newArr);// [1, 2, 3, 4]

arr.reduceRight(callback, initialValue) 与arr.reduce()功能一样,不同的是,reduceRight()从数组的末尾向前将数组中的数组项做累加。


模块化

模块化是指将一个大的程序文件,拆分成为许多小的文件,然后将文件组合起来。

模块化的好处

  • 防止命名冲突
  • 代码复用
  • 高维护性

ES6模块化语法

模块功能主要由两个命令构成:export和import

  • export命令用于规定模块对外的接口
  • import命令用于输入其他模块提供的功能

默认导入导出

默认导出语法 export default 默认导出成员

//当前文件模块为ml.js
//定义私有成员a和c
let a = 10
let c = 20
//外界访问不到变量d 因为它没有被暴露出去
let d = 30
function show(){
  //将本模块中的私有成员暴露出去 供其他模块使用
  export default{
    a,
    c,
    show
  }
}

默认导入语法 import 接受名称 from ‘模块标识符’

//导入模块成员
import ml from '.ml.js'
console.log(ml)
//打印结果为
//{a:10, c:20, show:[]function:show]}

按需导出 与 按需导入

按需导出语法 export let s1 = 10

//当前文件模块为m1.js
//向外按需导出变量 s1
export let s1 = 'aaa'
//向外按需导出变量s2
export let s2 = 'bbb'
//向外按需导出方法 say
export function say = function(){}

按需导入语法 import { s1 } from ‘模块标识符’

//导入模块成员
import {s1, s2 as ss2, say} from './m1.js'
 
console.log(s1)		//打印输出aaa
console.log(s2)		//打印输出bbb
console.log(say)		//打印输出[function:say]

async 与 await

async 和 await 两种语法结合,可以让异步代码看起来像同步代码。

async

async 函数返回值为 promise 对象
promise 对象结果由 async 函数执行的返回值决定

await

await 函数必须写在 async 函数中
await 右侧的表达式一般为 promise 对象
await 返回的是 promise 的成功值
await 的 promise 失败了,就会抛出异常,需要通过 try…catch 捕获处理

读取文件

const fs = require('fs')

let p1 = new Promise((res,rej)=>{
    fs.readFile('./file.md',(err,data)=>{
        if (err) rej(err)
        res(data)
    })
})

let p2 = new Promise((res,rej)=>{
    fs.readFile('./file2.md',(err,data)=>{
        if (err) rej(err)
        res(data)
    })
})

async function fileread(){
    let n1 = await p1
    let n2 = await p2
    console.log(n1.toString()) //我是文件1里的数据
    console.log(n2.toString()) //我是文件2里的数据
}

fileread()

Ajax

function Ajax(type='GET', url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.open(type, url)
    xhr.send()
    xhr.onreadystatechange = function () {
      if(xhr.readyState === 4) {
        if(xhr.status >= 200 && xhr.status < 300) {
          resolve(xhr.response)
        }else {
          reject(xhr.status)
        }
      }
    }
  })
}

async function xmlajax() {
  const res1 = await Ajax('GET', 'https://www.baidu.com')
  const res2 = await Ajax('GET', 'https://www.sina.com')
  console.log(res1)
  console.log(res2)
}

xmlajax()
  

可选链操作符

?. 当对象层数比较深,使用可选链操作符,可以免去层级判断。

如果不使用可选链操作符,一般使用 && 来连接判断

let data = {
    d1:{
        d2:{
            id:'123'
        }
    }
}

let db = data && data.d1 && data.d1.d2 && data.d1.d2.id
console.log(db) //123

let content = data?.d1?.d2?.id
console.log(content) //123

参考文章:
https://segmentfault.com/a/1190000020934044
https://segmentfault.com/a/1190000020889508
https://www.cnblogs.com/hexiaobao/p/12108572.html

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