1.新增关键词letconst

  • ### letconst声明的是块级作用域

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

  var a=1;
  {
      var a=1;
  }
  console.log(a) // 0

  //假设有5个button标签
  var allBtns=document.querySelectorAll("button");
  for( var i=0;i<allBtns.length;i++ ){
      allBtns[i].onclick=function(){
          console.log(i);
      }
  }

解决方法一

  //假设有5个button标签
  var allBtns=document.querySelectorAll("button");
  for( var i=0;i<allBtns.length;i++ ){
      (function(i){
          allBtns[i].onclick=function(){
              console.log(i);
          };
      })(i)
  }

解决方法二

  //假设有5个button标签
  var allBtns=document.querySelectorAll("button");
  for( var i=0;i<allBtns.length;i++ ){
      allBtns[i].onclick=(function(i){
          return function(){
              console.log(i);
          }
      })(i);
  }

而使用let定义,则可以方便很多

解决方法三

  //假设有5个button标签
  var allBtns=document.querySelectorAll("button");
  for( let i=0;i<allBtns.length;i++ ){
      allBtns[i].onclick=function(){
          console.log(i);
      }
  }
  /*
  *因为let定义的是块级作用域变量
  *每当循环一次,会产生一个新的块级作用域
  *每个作用域的变量互不影响,互不干扰
  */

  • ### letconst不能像var一样变量提升

  console.log(a) //undefined
  var a=1;
  console.log(b) //ReferenceError: Cannot access 'b' before initialization
  let b=1;

  • ### 在当前作用域内,使用变量不能再letconst定义之前,叫做暂时性死区(TDZ)

  {
      console.log(a) //undefined
      var a=1;
  }
  {
      console.log(a) //ReferenceError: Cannot access 'a' before initialization
      let a=1;
  }

这样子导致了typeof函数不再是一个安全的函数了

  {
      typeof(a); //"undefined"
      var a=0;
  }
  {
      typeof(a); //ReferenceError: Cannot access 'a' before initialization
      let a=0;
  }

有些短暂性死区比较隐蔽

  function fun(a=b,b=2){
      return a+b;
  }
  fun(1,2) //报错

但是需要注意的是,出现这种情况,是需要在没有给函数传参的情况下,下面这种情况就不会了

  function fun(a=b,b){
      return a+b;
  }
  fun(1,2);  //3

  • ### letconst不允许重复定义

  let a;
  let a; //SyntaxError: Identifier 'a' has already been declared
  const PI=3.14;
  const PI=3.14; //SyntaxError: Identifier 'PI' has already been declared

  • ### const初始化定义后,必须马上赋值

  const PI; //SyntaxError: Missing initializer in const declaration

  • ### const不允许重新定义的是指向地址值保存的数据

  const obj={
      name:"jpc",
      age:19
  }
  obj.name="qqq"; //正常赋值
  console.log(obj.name) //qqq

  • 常量声明最好使用大写
  • ### ES5到ES6函数作用域的变化[特别注意]

  //ES5环境
  function fun(){
      console.log("I am outside");
  }
  (function(){
      if(false){
          function fun(){
              console.log("I am inside");
          }
      }
      fun() // I am inside
  })();

  //ES6环境
  function fun(){
      console.log("I am outside");
  }
  (function(){
      if(false){
          function fun(){
              console.log("I am inside");
          }
      }
      fun() // TypeError: fun is not a function
  })();

原理

  //ES6环境
  function fun(){
      console.log("I am outside");
  }
  (function(){
      let fun=undefined;
      if(false){
          function fun(){
              console.log("I am inside");
          }
      }
      fun() // TypeError: fun is not a function
  })();

2.解构赋值

  • ### 普通数组的解构赋值

  var arr=[1,2,3];
  var [a,b,c]=arr;
  console.log(a,b,c) // 1,2,3

  var arr=[1,[1,2],3];
  var [a,b,c]=arr;
  console.log(a,b,c) // 1,[1,2],3

  var arr=[1,[1,2],3];
  var [a,[b],c]=arr;
  console.log(a,b,c) // 1,1,3

  var arr=[1,2,3];
  var [a,b,c,d]=arr;
  console.log(a,b,c,d) // 1,2,3,undefined

支持默认值得设置

  var arr=[1,2,3];
  var [a,b,c,d=4]=arr;
  console.log(a,b,c,d) // 1,2,3,4

特别注意的是

  var arr=[1,2,3,undefined];
  var [a,b,c,d=4]=arr;
  console.log(a,b,c) // 1,2,3,4
  /*
  *当赋值的值为undefiend基本数据类型,且有默认值,则赋值的还是默认值
  */

  var arr=[1,2,3,null];
  var [a,b,c,d=4]=arr;
  console.log(a,b,c) // 1,2,3,null
  /*
  *null不受影响
  */

##### 数组的解构赋值只要支持Iterator接口的对象,都可以进行解构赋值,基本数据类型不支持数组的解构赋值

  var [a,b,c]=new Set([1,2,3]);

##### 默认值为表达式时,是惰性求值,只有当赋值的值为undefined时,才会调用

  var [a=f()]=[0];
  console.log(a) //0
  function f(){
      console.log("我执行了")// 不执行
  }

  • ## 对象的解构赋值

  var obj={
      name:"jpc",
      age:19
  }
  var {name,age,sex}=obj;
  console.log(name,age,sex); //"jpx" 19 undefined

##### 对象是依据键名配对的方式进行解构赋值的,所以obj对象内没有sex属性,sex变量的值就为undefined

  const { log } = console;
  log('hello') // hello

##### 如果变量名与属性名不同,但是又需要赋值,可以如此解决

  /*
  *前提
  *把obj对象内的firstName属性和lastName属性赋值到全局变量first和last中
  */
  var obj={
      firstName:"Mark",
      lastName:"Cheng"
  }
  var {firstName:first,lastName:last}=obj;
  console.log(first,last); //Mark Cheng

  /*
  *这样子的写法我现在很不能够理解,我认为应该这么写的
  *var {first:firstName,last:lastName}=obj;
  *貌似又理解了
  */

  • ### 字符串的解构赋值

  var str="abcd";
  var [a,b,c]=str;
  console.log(a,b,c) // a b c

##### 当一个变量被定义了,然后在解构赋值,可能会报错,可以在外加一个圆括号解决

  var x;
  ({x} = {x: 1});

3.函数的扩展&箭头函数

  • ### 箭头函数基本写法

  var fn=(a,b)=>{ return a+b; };
  console.log(fn(1,2)); //3

  var fn=(a,b)=>a+b;
  console.log(fn(1,2)); //3

  var fn=(a,b)=>({a:a,b:b});
  console.log(fn(1,2)); //{a:1,b:2}

  var fn=a=>{ return a; };
  console.log(fn(1)); //a

  • 箭头函数的this指向代码所在对象的对象,且不能指定this
  • 箭头函数内没有arguments对象
  • 箭头函数作为构造函数使用
  • 不能使用yield命令,不能做Generator函数
  • ### ES6中,函数可以设置默认参数了

  function fun(a=0,b=0){
      return a+b;
  }
  console.log(fun()) // 0

传统写法

  function fun(a,b){
      a= a || 0;
      b= b || 0;
      return a+b;
  }
  console.log(fun()) // 0

  let fun2=({a,b})=>{
      console.log(a,b);
  }
  fun2();//TypeError: Cannot destructure property 'a' of 'undefined' as it is undefined.

  /*
  *函数fun2需要输入一个对象参数,而我们调用函数的时候,
  *由于没有传值,所以参数值为undefined,
  *而函数内部调用对象参数下的a属性时,
  *undefiend.a的形式,所以报错
  */

  • ### 如果函数尾参数不设置参数,而在非尾位置设置了参数没有意义的

  function fun(x=1,y=10,z){
      return x+y+z;
  }
  fun(7,8,9); //24
  /*
  *此时,就算非尾参数都设置了默认值,但是没有意义
  */

  • 函数的length属性
    这个属性计算的是实参传入的个数,即除去含默认值的参数之后的参数总数

  function fun(a,b,c){

  }
  fun.length // 3
  function fun2(a,b,c=0){
      //
  }
  fun2.length //2

  • rest参数(...变量名)
    可以为函数获取到剩余的参数,并转换为真数组,及时没有剩余的参数,也会转换为空数组
    rest参数之后不允许有其他参数了

  function fun(x,y,...all){
      console.log(all); //[2,3,4,5]
      console.log(Object.prototype.toString.call(all)); //"[object Array]" 
  }
  fun(0,1,2,3,4,5);

  • 函数内使用了严格模式,那么参数默认值、解构赋值和rest参数都不能设置了
    这里需要注意的是,是在函数内使用了严格模式,即函数外作用域(或全局)设置了严格模式,是不受到影响的

  //设置参数默认值的报错
  function fun(x=0){
      "use strict";
      console.log(x) //报错
  }
  fun(1);

  //设置解构赋值的报错
  function fun({x}){
      "use strict";
      console.log(x) //报错
  }
  fun({x:1});

  //设置rest参数的报错
  function fun(...x){
      "use strict";
      console.log(x) //报错
  }
  fun(0,1,2,3);

  //不在函数内使用严格模式,无报错,正常运行
  "use strict";
  function fun(x=0){
      console.log(x) //1
  }
  fun(1);
  function fun({x}){
      console.log(x) //1
  }
  fun({x:1});
  function fun(...x){
      console.log(x) //[0,1,2,3]
  }
  fun(0,1,2,3);

  • 函数的name属性
  • 具名函数返回的实际的函数名,不管是否赋值给了变量
    function fun(){} fun.name // fun let fun3=function fun2(){} fun3.name //fun2
  • 匿名函数赋值给变量后,ES5中返回的是空字符串(""),ES6中返回的是变量名称
    ES5环境 let fun=function (){} fun.name //""
    ES6环境 let fun=function (){} fun.name //"fun"
  • 尾调用--函数return一个函数调用
  • 尾递归--函数return调用自身
  • ### 柯里化--把多参数的函数转换成单参数函数的形式

4.迭代器、生成器

  • Iterator迭代器
    数组,字符串,
  • Generator生成器
    yield必须包含在当前函数内,不能在嵌套函数内
  • ##### 如果在另外一个表达式内,必须要加上括号

  function* demo() {
    console.log('Hello' + yield); // SyntaxError
    console.log('Hello' + yield 123); // SyntaxError

    console.log('Hello' + (yield)); // OK
    console.log('Hello' + (yield 123)); // OK
  }

  • #### 如果作为函数的参数不需要括号

  function *Gen4(x,y){
      console.log(yield x,yield y);
      return x+y;
  }
  var f4=Gen4(5,8);
  f4.next() // {value:5,done:false}
  f4.next() // {value:8,done:false}
  f4.next() // undefined undefined
            // {value:13,done:true}

  • #### Generator.prototype.return

  function *Gen2(){
      yield 1;
      yield 2;
      yield 3;
      return "over";
  }
  var f2=Gen2();
  f2.next() //{ value:1,done:false }
  f2.return(99) //{ value:99,done:true }
  f2.next() //{ value:undefined,done:true }

  • #### 如果里面有try{}finally{}语句,当在执行try语句的代码块的时候,使用return方法,会直接进入到finally的语句中,如果没有return,最后的是提供的return值,否则就是默认的

  function *Gen3(){
      yield 1;
      yield 2;
      try{
          yield 3;
          yield 4;
      }finally{
          yield 5;
          yield 6;
          return 7;
      }
      return 8;
  }
  var f3=Gen3();
  f3.next();  // {value: 1, done: false}
  f3.next();  // {value: 2, done: false}
  f3.next();  // {value: 3, done: false}
  f3.return(99) //{value: 5, done: false}
  f3.next();  // {value: 6, done: false}
  f3.next();  // {value: 7, done: true}

  function *Gen3(){
      yield 1;
      yield 2;
      try{
          yield 3;
          yield 4;
      }finally{
          yield 5;
          yield 6;
      }
      return 8;
  }
  var f3=Gen3();
  f3.next();  // {value: 1, done: false}
  f3.next();  // {value: 2, done: false}
  f3.next();  // {value: 3, done: false}
  f3.return(99) //{value: 5, done: false}
  f3.next();  // {value: 6, done: false}
  f3.next();  // {value: 99, done: true}

  function *Gen3(){
      yield 1;
      yield 2;
      try{
          yield 3;
          yield 4;
      }finally{
          yield 5;
          yield 6;
          return 7;
      }
      return 8;
  }
  var f3=Gen3();
  f3.next();  // {value: 1, done: false}
  f3.next();  // {value: 2, done: false}
  f3.next();  // {value: 3, done: false}
  f3.next();  // {value: 4, done: false}
  f3.next();  // {value: 5, done: false}
  f3.next();  // {value: 6, done: false}
  f3.next();  // {value: 7, done: true}

  • Generator函数内调用Generator函数的方法(加*号)
    function *genOuter(){ yield 0; yield *genInner(); yield 6; } function *genInner(){ yield 1; yield 2; yield 3; yield 4; yield 5; } var gen=genOuter(); gen.next(); //{ value:0,done:false } gen.next(); //{ value:1,done:false } gen.next(); //{ value:2,done:false } gen.next(); //{ value:3,done:false } gen.next(); //{ value:4,done:false } gen.next(); //{ value:5,done:false } gen.next(); //{ value:6,done:false } gen.next(); //{ value:undefined,done:true }
    不加*号
    function *genOuter(){ yield 0; yield genInner(); yield 6; } function *genInner(){ yield 1; yield 2; yield 3; yield 4; yield 5; } var gen=genOuter(); gen.next(); //{ value:0,done:false } gen.next(); //{ value:getInner,done:false } (一个遍历器对象) gen.next(); //{ value:6,done:false } gen.next(); //{ value:undefined,done:true }
  • Generator函数不能做为构造函数,否则会报错
    function* F() { yield this.x = 2; yield this.y = 3; } new F() // TypeError: F is not a constructor

5.小总结

  • Promise是为了解决回调函数多层嵌套、不易于开发维护的结果,回调地狱
  • Generator可以让事件暂停,去做另外一件事,然后继续做这件事
  • asyncGenerator的语法糖,更加自动化,更加便捷
  • 可原生迭代的 map,set,字符串,数组,dom节点

6.Promise

resolve-->成功

reject-->失败

catch

finally

Promise.all全部完成后调用

Promise.race竞争,只处理一个结果

示例代码

  • Promise可以进行链式调用,如果第一个then不自定义返回的Promise实例对象,则第二个then获取的一个新的Promise且为resolve状态