ES6新特性(自用)

发布时间 2023-12-10 19:08:23作者: Mio_Huang

ES6新特性

ES6发行自2015年,主要的新特性包括:模块化、面向对象语法、Promise、箭头函数、let、const、数组解构赋值等等(还有Async/await 关键字)

 

let、const关键字;变量解构赋值;模板字符串

let关键字(最好用let替代var)

  1. 和var一样,可以初始化多种类型的变量(数字、字符串、数组、对象),同var

  2. 块级作用域,作用域外不可被引用,同var

  3. 不存在变量提升,变量创建前,使用变量会报错(但是var可以,会取到undefined)

  4. 不会影响作用域链,按照js执行顺序执行,同var

//let可以初始化不限类型的变量
   let a = 1;
   let b,c,d;
   let e = [];
   let times = new Date;
   console.log(times)
//3.不存在变量提升
   console.log(ee)//undefined
   var ee = 114
   console.log(ww)//报错
   let ww = 514  

const关键字(常量)

  1. const关键字是“常量”

  2. const变量名默认要大写,例如const PI=3.14

  3. 对于简单数据类型的const变量,不允许修改,修改会报错。但是对于数组和对象类型的变量,没有这一项约束

变量的解构赋值

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

  • 数组对多变量赋值:

    //数组的解构赋值
    const arr = ['张学友', '刘德华', '黎明', '郭富城'];
    let [zhang, liu, li, guo] = arr;
  • 对象对多变量赋值

    //对象的解构赋值
    const lin = {
    name: '林志颖',
    tags: ['车手', '歌手', '小旋风', '演员'],
    drive = function(){
    alert("drive my Tesla")
    }
    };
    let {name, tags, drive} = lin;//分别赋值到字符串、数组、函数

模板字符串(``超级字符串)

在这章节内容里,我会使用传荣字符串型和``模板字符串做对比

  1. 支持多行内容,内容里可以直接出线换行符(string只能用转义符号或者字符串拼接做到这一点)

  2. 方便的变量拼接 ,使用${变量名}xxx即可实现变量拼接(比String的加号好用)

//1.支持多行
let str1="string只能输出一行内容口牙"
let sstr1=`Super字符串
           想写几行就写几行
           <ul>
               <li>爽!</li>
           </ul>`
//2.方便的字符串拼接
   let newStr1 = "New "+str1
   let superPlus = `New ${str1} Day`

简化的对象写法

ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。

注意:对象简写形式简化了代码,所以以后用简写就对了

let name = '尚硅谷';
let slogon = '永远追求行业更高标准';
let improve = function () {
console.log('可以提高你的技能');
}
//属性和方法简写
let atguigu = {
    name,
    slogon,
    improve,
    change() {
    console.log('可以改变你')
    }
};

箭头函数fn=()=>{}(重点)

  1. 箭头函数 this 指向声明时所在作用域下 this 的值 (这条非常重要)

  2. 函数不能作为构造函数实例化

  3. 不能使用 arguments

  4. 如果形参只有一个,则小括号可以省略

  5. 函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果

//箭头函数和普通函数对比
   let fn = function(e){
   console.log(this)
   console.log(e.target)
  }
   let fn_plus = (e)=>{
   console.log(this)
   console.log(e.target)
  }
//1.箭头函数和普通函数this的指向不同
   buttons = document.querySelectorAll("button")
   buttons[0].addEventListener("click",fn)         //返回调用这个函数的对象
   //返回<button>111</button>
   buttons[1].addEventListener("click",fn_plus)    //返回函数在声明时所在作用域的对象
   //返回window对象,要定位到调用这个函数的元素,需要用e.target
   
//两种简写情况
   ////只有一个形参,可以省略小括号
   let plusme = a=>{return a+a}
   ////只执行一条语句,可以省略大括号和return
   let addTwo = (a,b)=> a+b;

//两种不能这么写的情况
   let fn2 = ()=>{
       //arguments存储了 函数传递过来的所有实参,但是在箭头函数里不能这么写
       console.log(argument.length)  
  }
   let Person = (name,age)=>{
       //构造函数的写法是这样,但是箭头函数不能用来写箭头函数
       //报错信息:Person is not a constructor
       this.name = name
       this.age = age
  }
   let zhang3 = new Person("ZhangSan",23)//会报错

箭头函数适合与this无关的回调.定时器,数组的方法回调 箭头函数不适合与this有关的回调.事件回调,对象的方法

rest参数...args

它是用来替代箭头函数中无法使用arguments的问题的,但是需要注意一点,它只能放在形参中的最后一项

//rest 参数必须是最后一个形参
function minus(a,b,...args){
console.log(a,b,args);
}
minus(100,1,2,3,4,5,19);

... 拓展运算符

...扩展运算符能将『数组」转换为逗号分隔的『参数序列」

可以用于函数传参与展开对象

//1.拓展运算符可以向函数传递多个参数
   let someList =[1,2,3,4]
   let fn = (...arg)=>{
   console.log(arg)
  }
   fn(someList)    //Array(4)
   fn(...someList) //(4) [1,2,3,4]
//2.拓展运算符可以展开对象
////注意,要展开的对象中只能包含简单数据类型,像数组、对象、函数什么的就不行
   let band = {
   mumber:4,
   // who : ["鼓手","吉他手","键盘手","主唱"],
   // instrument :["架子鼓","吉他","贝斯"],
   name:"结束乐队"
  }
   console.log(band)   //{mumber: 4, name: '结束乐队'}

Symbol一种新的不可运算的数据类型

Symbol是一种不可修改的数据类型,属于js语言中第七种基本数据类型

七种数据类型依次为:Undefined 、String 、Symbol 、Object 、Null 、Number 、Boolean

口诀:USONB (you are so NB)  
U undefined  
S string symbol
O object  
N null number
B boolean  
  • 创建Symbol

//创建symbol的两种方法
////比较搞笑的是,s2不等于s3,因为初始化方法不一样
   let s2 = Symbol('尚硅谷');
   let s3 = Symbol.for('尚硅谷');
//symbol变量的内容和类型
console.log(s2, typeof s2); //Symbol(尚硅谷) 'symbol'
  • 在对象中使用Symbol函数

    好处在于使用symbol封装的函数非常安全

    //Symbol是一个独一无二的值,这是给对象添加属性的示例
       let game = {}
       let method = {
           up:Symbol(),
           down:Symbol()
      }
    //symbol为对象内函数赋值
       game[method.up] = function(){
           console.log("我可以改变形状")
      }
       game[method.down] = function(){
           console.log("我可以快速下降")
      }
       console.log(game)//我靠,咋用啊这个
    //symbol为对象赋值的另一个例子
       let youxi = {
           name:"狼人杀",
           // say : function(){console.log("speak something")}
           // 由于Symbol是一个表达式,所以这种场景应该加上中括号
          [Symbol('say')]:function(){
               console.log('say something')//我靠,咋用啊这个
          }
      }
    //Symbol内置属性:可以起到一个拓展对象功能的作用
       const arr2 = [4,5,6];
    arr2[Symbol.isConcatSpreadable]=false;

迭代器 Symbol(Symbol.iterator):

任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。经过观察,每一个可以迭代的对象都有一个这个属性:Symbol(Symbol.iterator): ƒ values()

原生具备 iterator 接口的数据(可用 for xxx of 遍历)

真别写成for xxx in 了

ArrayArgumentsSetMapStringTypedArrayNodeList

让我们仿照迭代器的工作原理,自己写一个迭代器

        const banji = {
           name: "医信一班",
           stus: ['jim','bob','mike','jenny'],
          [Symbol.interator](){
               //索引变量
               let index = 0
               //this纸箱banji这个对象
               let _this = this
               return{
                   next:function(){
                       //如果索引值小于数组长度
                       if (index< _this.stus.length){
                           const result = {value:_this.stus[index], done:false}
                           //下标自增
                           index++
                           //返回结果
                           return result
                      }else{
                           return {value:undefined ,done:true}
                      }
                  }
              }
          }
      }
       
       //现在可以遍历这个对象了
       //我真是活见了赛博幽灵了,为什么in 和 of关键词不一样啊
       for (let v of banji){
           console.log(v)
      }

生成器

生成器其实就是一个特殊的函数,它有以下几个特点

  1. 星号要放在函数关键字和函数名中间,只要放在中间就行

  2. yield是函数代码的分隔符,它将函数代码分割成几部分

  3. 调用时返回一个迭代器对象,使用.next()方法执行下一步

//生成器其实就是一个特殊的函数
//对于异步编程来说,function都是纯回调函数,例如node fs ajax mongodb

//1.星号要放在函数关键字和函数名中间,只要放在中间就行
//2.yield是函数代码的分隔符,它将函数代码分割成几部分
   function *gen(){
       console.log(111);
       yield '一只没有耳朵';
       console.log(222);
       yield '一只没有尾部';
       console.log(333);
       yield '真奇怪';
       console.log(444)
  }
//执行结果比较特殊,会返回一个迭代器对象,而不是里面的语句
   let iterator =gen();
   console.log(iterator)//会输出一个迭代器对象
   iterator.next();//111,只有使用next方法才能让它继续执行
   iterator.next();
   iterator.next();
   iterator.next();
//一种遍历方法
   for(let v of gen()){
       console.log(v);
       //111 '一只没有耳朵' ...
  }

生成器是可以传参的

具体的在于,只有进行第一次next().后,才会执行函数体内语句

但是第二次next(参数).就可以携带参数到第一个yield里面了,见例子:

function *gen(a){
   console.log("begin");
   console.log(a)
   let one = yield '这里写啥其实不重要对吧';
   console.log(one);
   let two = yield '看不见';
   console.log(two);
}

//这里传参,是可以的,但是函数不会开始执行,需要一个next让生成器启动
let iterator = gen('AAA');
//第一次next不要传参,它相当于启动按钮,一直执行到下一个yield
iterator.next();
//第二次next可以开始传参了
iterator.next("n1");
iterator.next("n2");
//多写一个next实际上是没用的,什么也不会发生
//next数目应该等于 yield+1
iterator.next("n3");

生成器应用(简单的异步操作,按顺序执行)

在开发过程中,我们很有可能遇到多个异步操作,其中的嵌套关系非常复杂,这时候我们可以用生成器理清他的关系。

这个场景在于,实际开发中,下一步是由获取到前一步的参数为前提的,也就是前一步完成后才能触发下一步

//多个回调函数嵌套在一起,嵌套关系很混乱,我们不要这样子
// setTimeout(()=>{
//     console.log(111);
//     setTimeout(()=>{
//         console.log(222);
//         setTimeout(()=>{
//             console.log(333)
//         },3000);
//     },2000);
// },1000) //111 222 333

//试着用生成器按步骤执行的特定重写一下异步函数
function one(){
   setTimeout(()=>{
       console.log(111);
       //由第一个函数执行成功后,启动第二个函数
       iterator.next();
  },1000)
}
function two(){
   setTimeout(()=>{
       console.log(222);
       iterator.next();
  },222)
}
function shree(){
   setTimeout(()=>{
       console.log(333)
  },333)
}

//设置生成器,由第一个函数用next触发第二个函数,以此类推
function *gen(){
   yield one();
   yield two();
   yield three();
}
//让生成器启动
let iterator = gen();
iterator.next();

Promise(重点)

在没有promise之前,我们都用上面的生成器的方案来执行异步操作,有了promise后,情况就大为改观了。

在语法上promise是一个构造函数,用来封装异步操作并可以获取器成功或者失败的结果。

当正确执行异步操作时,使用resolve()以进入到then的成功状态,执行异步操作失败时,使用reject()以进入到then的失败状态

//实例化的时候,接收一个函数类型的值,接受两个形参(默认形参名为:resolve,reject)
////函数体里面封装一个异步的操作
const p = new Promise(function(resolve,reject){
   setTimeout(function(){
       let data = '数据库中的用户数组'
       //当调用玩resolve函数后,p的状态就会为成功(promise的三种状态:初始化/成功/失败)
       resolve(data)
       // let err = "数据读取失败了啊啊"
       // //当调用reject函数后,p的状态就会为失败,同时进入到then方法中
       // reject(err)
  },1000)//
});

//当p的状态为成功/失败后,就可以进入到then方法中
////then方法的形参是两个函数,成功函数放在第一个的形参是value,失败函数放在第二个的形参是reason
p.then(function(value){
   //value是resolve传递来的值
   console.log(value);
},function(reason){
   //reason是reject传递来的值
   console.log(reason)
})

Promise的链式调用

调用then方法 then方法的返回结果是Promise对象,对象状态由回调函数的执行结果决定 如果回调函数中返回的结果是非promise类型的属性,状态为成功,返回值为对象的成功的值

//创建promise对象
const p = new Promise((reslove,reject)=>{
   setTimeout(()=>{
       // reslove('用户数据调用成功');
       reject("ERR Occure")
  },1000)
})

//then
let result = p.then(value =>{
   console.log(value);
   // return 123
},reason=>{
   console.warn(reason);
   //throw 'AAA'//这个会抛出一个自定义报错,相当于python里的raise
})
console.log(result) //不管then有没有返回值,都会返回resolve()或者reject()里面携带的内容

基于then函数会返回一个promise对象的特性,我们可以写出promise的链式调用

//链式回调
p.then(value=>{
   //这里可以再写一个promise
}).then(value=>{

}).then(value=>{
   //每个then执行完后,反正是一个Promise对象,因此可以一遍又一遍的嵌套异步操作
})

catch语法糖

catch和then的用法一样,是专门用来捕获promise异步错误的

//创建promise对象
const p = new Promise((reslove,reject)=>{
   setTimeout(()=>{
       // reslove('用户数据调用成功');
       reject("ERR Occure")
  },1000)
})

p.then(function(value){},function(reason){})

p.catch(function(reason){
   console.warn(reason)
})

Set集合

  • 集合Set基本操作

//声明一个 set
let s = new Set();
//集合set初始化时,可以接受传入一个可迭代数组
let s2 = new Set([1,5,2,1,3,6,5])
console.log(s2,typeof(s2))//会自动帮你去重

//元素个数
console.log(s2.size)
//添加新的元素
s2.add(14)
//删除元素
s2.delete(6)
//检测,存在输出true、不存在输出flase
s2.have(5)
s2.have(114514)
//清空
s2.clear
//遍历(使用for xxx of)
for(let v of s2){
   console.log(v);
}
  • 集合Set进阶操作

去重、交并差集

//1.数组去重
let arr = [1,3,5,7,9,9,5,3,1]
let result = [...new Set(arr)]
console.log(result)
//2.交集
////我靠,为什么我连数组的filter方法都不会啊???
//3.并集

//4.差集

Map

Map数据结构它类似于对象,键值对的集合。但是“键”不限于字符串,可以是各种数据类型。

并且集成了。。。可以使用for of进行遍历

let m = new Map();
// 添加元素
////这三个例子是想告诉你,map的set方法,键可以是任何是数据类型,值可以是数据或者函数
m.set('name','尚硅谷')
m.set('change',function(){
console.log("66666")
})
let key={
school:'ATGUIGU'
}
m.set(key,['Shanghai','Beijing'])

// size 显示map长度。 m.size>>>3

//删除
m.delete('name')
//清空
m.clear()

//查询
m.get('change') //输出一个函数
m.get(key) //可以使用对象作为键,查询值

class

class介绍与初体验

传统方法写构造函数

//传统方法写构造函数
function Phone(brand,price){
this.brand = brand;
this.price = price
}
//添加构造函数的方法,建议使用原型对象进行添加,这样可以做到重用
Phone.prototype.calling = function(){
console.log("I can call via my phone")
}

//实例化对象
let Huawei = new Phone("Huawei",5999)
Huawei.calling();   //调用函数成功

class方法写构造函数(constructor)

//class方法写这个函数
class Phone{
   //构造方法,名字是固定的,会在使用new Phone的时候自动执行
   constructor(brand,price){
       this.brand = brand;
       this.price = price;
  }
   //class里面必须使用标准的函数写法,支持箭头函数
   calling(){
       console.log(666666)
  }
   // calling = ()=>{
   //     console.log(77777)
   // }
}
let Huawei = new Phone("Huawei",5999)
Huawei.calling();   //调用函数成功

class静态方法

静态对象的定义:

//传统的函数静态对象,示例对象,是没有构造函数对象那样的属性的
function Phone(){

}
Phone.name="手机";
Phone.change = function(){
   console.log(66666)
}

let nokia = new Phone();
console.log(nokia.name) //undefined (即实例对象nokia的属性和静态对象Phone不通用)
//如果想通用的话,用Phone.prototype.name="手机";

class实现静态对象 static

对于static标注的方法,它属于类但是不属于实例对象

class Phone{
   //静态属性
   static name = '手机';
   static change(){
       console.log("change the world")
  }
}
//实例化对象,发现无法使用类里面的静态属性
//对于static标注的方法,它属于类但是不属于实例对象
let nokia = new Phone()
console.log(nokia.name)
console.log(Phone.name)

构造函数实现继承

ES5中有构造函数的继承,主要步骤有二:

//使用call方法改变this的值,此时this指向SmartPhone的实例对象 Phone.call(this,brand,price)

//这样子级构造函数的实例对象,里面就会有父级的方法 SmartPhone.prototype = new Phone;

//父级构造函数
       function Phone(brand,price){
           this.brand = brand;
           this.price = price
      }
       //添加构造函数的方法,建议使用原型对象进行添加,这样可以做到重用
       Phone.prototype.calling = function(){
           console.log("I can call via my phone")
      }

       //子级构造函数
       function SmartPhone(brand,price,color,size){
           //使用call方法改变this的值,此时this指向SmartPhone的实例对象
           Phone.call(this,brand,price)
           this.color = color;
           this.size = size;
      }
       //设置子级构造函数的原型prototype
       //这样子级构造函数的实例对象,里面就会有父级的方法
       SmartPhone.prototype = new Phone;
       //对子级构造函数的构造器做一个矫正(下面这行代码不加也不会影响)
       SmartPhone.prototype.constuctor = SmartPhone;

       //声明子类对象的方法
       SmartPhone.prototype.photo = function(){
           console.log("I can take a picture")
      }
       //实例化对象
       const chuizi = new SmartPhone('锤子',2499,'black','ProMax')
       console.log(chuizi)

类的继承

//super函数以调用父类中的方法(究极方便啊) super(brand,price)

class Phone{
   //构造方法
   constructor(brand,price){
       this.brand = brand;
       this.price = price;
  }
   //父类的成员属性
   calling(){
       console.log("I can calling for you")
  }
}

//用extend继承父类中的方法
class SmartPhone extends Phone{
   //构造方法
   constructor(brand,price,color,size){
       //super函数以调用父类中的方法
       super(brand,price)
       this.color = color;
       this.size = size;
  }

   //子类里独有的方法
   photo(){
       console.log(6666666)
  }
}

const Xiaomi = new SmartPhone('Xiaomi',1999,'blue','4.7inch')
console.log(Xiaomi)
Xiaomi.calling()
Xiaomi.photo()

如果子类和父类有同名方法的话,子类的方法会覆盖掉父类的方法,称之为“子类对父类的方法重写”

Class里的get 和set

  • 当对某一属性进行获取时,执行get price函数里面的代码(应用场景:实时修改数据)

  • 当对某一属性进行设置时,执行set price方法里的函数(应用场景:表单输入数据合法判断)

//class类里的get和set
// 当对某一属性进行获取时,执行get方法里面的函数
// 当对某一属性进行设置时,执行set方法里的函数
class Phone{
   //只要price属性被读取,就执行get price函数里面的代码,且这个函数里面的返回值,就是这个price属性的值
   get price(){
       console.log('价格属性被读取了')
       return 114514
  }

   // 只要price属性被修改,就执行set price函数里面的代码
   set price(whatever){
       //值得注意的是,这个函数必须调用一个参数
       console.log('价格属性被修改了')
  }
}
//实例化对象
let s = new Phone();
console.log(s.price);
s.price = 'free'
console.log(s)

ES6里的数值拓展

  • Number,EPSILON是JavaScript表示的最小精度 EPSIL0N属性的值接近于2.2204460492503130808472633361816E-16 主要用于浮点数近似相等
    function equal(a,b){
       //判断两数差值是否小于最小精度,小于则判断为相等
       if(Math.abs(a-b)<Number.EPSILON){
           return true;
      }else{
           return false;
      }
    }
    conso1e.log(0.1+0.2==0.3);//false
    conso1e.log(0.1+0.2,0.3);//true    
  • 二进制和八进制
    let b = 0b1010; //2进制,10
    let o = 0o777; //8进制,511
    let d = 100; //10进制,没什么好说的
    let x = 0xff; //16进制,512
  • Number.isFinite 检测一个数值是否为有限数
  • Number,isNaN检测一个数值是否为NaN Number.isNaN(123)//输出false,表示不是NaN,是一个数
  • Number.parseInt Number.parseFloat字符串转整数
  • Number.isInteger判断一个数是否为整数
  • Math.trunc将数字的小数部分抹掉
  • Math.sign判断一个数到底为正数负数还是零 (返回1、0、-1)

ES6里对象方法的拓展

  1. Object.is 判断两个值是否完全相等

    和全等号===很像

  2. Object.assign 对象的合并

    Object.assign(对象1,对象2) //并集,如果属性重名,后面的对象会把前面的对象属性值覆盖掉

  3. Object.setprototypeOf 设置原型对象 Object.getprototypeOf (效率不高,一般不用)

ES6模块化import

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

  • 模块化的好处

    1.防止命名冲突

    2.提高代码复用性

    3.高维护性

模块化主要由以下两个命令:

  • export :主要用于规范模块的对外接口(暴露到外面)

  • import:用于输入其他模块提供的功能(引入别人的功能)

模块化(暴露export和引用import)

js文件里面写模块暴露:

  1. 分别暴露,在变量名前加关键词‘export’

  2. 集中暴露,export{变量名1,变量名2}

  3. 默认暴露,export default{ 里面写好变量初始化语句,变量、方法、对象都统一暴露出去}

//在需要暴露的对象前加‘export’
export let school = '尚硅谷'

export function teach(){
   console.log(666)
}
//集中暴露(用大括号把变量名围起来)
export {school,teach}
//默认暴露,在export default {}内写上数据,会自动全部暴露出去
////这就是vue2所使用的啊!!!太棒了
export default {
   //默认暴露,所有变量都存在default变量下面
   school: 'ATGuiGu',
   change: function(){
       console.log(5666666)
  }
   //可以是任意类型的
}

html标签中写模块引用,全部返回数据存储在m1这个变量里面

  1. 通用引入(通用,import {变量名} from '文件地址')

  2. 解构赋值引入(对应默认暴露default以及重名的引用变量名)

  3. 简便引入(只能针对默认暴露)

<script type="module">
   //1.通用的导入方式
   //将m1.js里面所有暴露的变量,全都输出到变量m1里面了
   import * as m1 from "./src/m1.js"
   console.log(m1)
   
   //2. 解构赋值形式(对应分别暴露)
   import {school,teach} from './src/m1.js'
   //// 可以命名以防止重名
   import {school as guigu,teach} from './src/m1.js'
   
   //3. 对应默认暴露,默认暴露所有变量都存储在m3下面,我们对default做一个别名设置
   //// 记住,不能直接使用default,必须取一个别名
   import {default as m3} from "./src/m1.js"
   
   //4. 简便暴露(只能针对默认暴露)
   import m3 from "./src/m1.js" //效果同上
</script>

ES6里面引入模块的新方法(一次性全引入)

html文件中:

<script src="./src/m1.js" type="module"> </script>

js文件中

import * as m1 from './src/m1.js'
import * as m2 from './src/m2.js'
import * as m3 from './src/m3.js'

//不用写export,html直接引入这个文件,就相当于依次执行了以上语句,非常方便且统一化

ES6模块化代码在项目中使用(使用babel将ES6语法转换为ES5)

由于ES6的import方式并不支持npm的一些库的一键引入,并且为了IE、火狐等浏览器的兼容性,我们一般会使用babel

  1. 安装工具 babel-cli(babel命令行工具) babel-env(预设包,能够把最新的ES特性转换为ES5) browserify(webpack替代品,做项目打包) npm i babel-cli babel-env browserify -e (为当前项目安装)

  2. 终端下使用 npx babel src -d dist/js --preset=base-preset-env 文件的目录 -d 存到哪个文件夹下 跟一个参数 3.源文件下js文件是原先的,dist文件夹下是babel转换后的结果

  3. 打包 npx browserify dist/js/app.js -o dist/bundle.js

    <!-- 
   1. 安装工具 babel-cli(babel命令行工具)
   babel-env(预设包,能够把最新的ES特性转换为ES5)
   browserify(webpack替代品,做项目打包)
   npm i babel-cli babel-env browserify -e (为当前项目安装)
   2. 终端下使用
   npx babel src -d dist/js --preset=base-preset-env
   文件的目录 -d 存到哪个文件夹下   跟一个参数
   3.源文件下js文件是原先的,dist文件夹下是babel转换后的结果
   4. 打包
   npx browserify dist/js/app.js -o dist/bundle.js
    -->
    <script src="dist/bundle.js"></script>

juqery包导入

app.js里面这样写:import $ from 包名

//修改背景颜色为粉色
//这是一个固定写法:import $ from 包名
import $ from 'jquery';//const $ = require("jquery");
$('body').css('background','pink');

然后依次执行:npx babel src -d dist/js --preset=base-preset-env

npx browserify dist/js/app.js -o dist/bundle.js

最后在html里面导入dist/bundle.js文件即可

ES7新功能(**幂运算和检测数组中存在某元素)

  1. includes

    //includes
    const mingzhu = ['西游记','红楼梦','水浒传','三国演义']
    console.log(mingzhu.incudes['西游记'])//true
  2. 幂运算 2**3=8

效果相当于Math.pow(2,3)

Async await 异步函数

初识Async

  1. Async函数的返回值是promise对象

  2. Promise的语法结构是Promise(function(resolve,reject){xxx}),promise里面是一个函数,切记

//写法就是在普通的函数前面加上async关键字,这样函数就会返回一个promise对象,如果返回的不是promise类型,也认为是promise成功状态。
////有助于方便的触发then函数
async function fn(){
   //throw抛出错误,结果是一个失败的promise
   ////throw new Error('出错啦')
   return new Promise(function(resolve,reject){
       // resolve('成功的数据')
       reject('失败的数据')
  })
}

const result = fn();
console.log(result)

//调用then方法
result.then(value =>{
   console.log(value)
},reason=>{
   console.log("ERR: ",reason)
})

await表达式

  1. await必须写在async函数中

  2. await右侧的表达式一般为promise对象

  3. await返回的是promise成功的值

  4. await的promise失败了,就会抛出异常,需要通过try...catch捕获处理

//创建Promise对象
const p = new Promise((resolve,reject)=>{
   // resolve('success')
   reject('ERROR')
})

//await要放在async函数中
async function fn2(){
   try{
       //await返回的是promise成功的值
       let result = await p;
       console.log(result)
  }catch(e){
       //await的promise失败了,就会抛出异常,需要通过try...catch捕获处理
       console.log(e)
  }
}
//切记要调用async函数
fn2()

用async实现Ajax请求

//发送AJAX请求,返回的结果是Promise对象
function sendAJAX(url){
   return new Promise((resolve,reject)=>{
       //1.创建对象
       const x = new XMLHttpRequest();
       //2.初始化
       x.open('GET',url)
       //3.发送
       x.send()
       //4.事件绑定
       x.onreadystatechange = function(){
           if(x.readyState ===4){
               if(x.status >= 200 && x.status<300){
                   //成功啦
                   resolve(x.response);
              }else{
                   //如果失败
                   reject(x.status)
              }
          }
      }
  })
}

//测试,能拿到结果,且是一个Promise对象
// console.log(sendAJAX('https://api.apiopen.top/getJoke'))

//Promise。then方法
// sendAJAX('https://api.apiopen.top/getJoke').then(value=>{
//     console.log("success!: ",value)
// },reason=>{
//     console.log("ERR: ",reason)
// })

//async,await方法
////用AJAX发请求,用await接请求,实在是非常方便啊
async function fn(){
   //发送AJAX请求,刚好await右侧需要一个promise对象
   let result = await sendAJAX('https://api.apiopen.top/getJoke')
   console.log(result)
}