ES6基础语法

1 let与const的区别

1.1 let与const的区别

  1. let与const都是只在声明所在的块级作用域内有效
  2. let声明的变量可以改变,值和类型都可以改变,没有限制。但是,let 不能重复定义一个变量,只能定义一次。
  3. const声明常量,不能改变,且声明的时候必须赋值,如果赋值的时候是一个对象,是可以改变的,因为指针指向的是对象,且没有发生改变,但对象里面的内容是可以发生改变的。

1.2 块作用域

  • { 该处为块作用域 },也就是说 你只需要用 {} 包起来,这个 {} 就是一个代码块

1.3 严格模式

  • es6中已经默认是严格模式
  • 声明方式:‘use strict
  • 例子
    'use strict’   
    function test(){
        for(leti=0;i<3;i++){
             console.log(i);
        }
        console.log(i);//报引用错误,因为采用了严格模式
    }
    test();
    

2 ES6之解构赋值

2.1 解构的概念

  • ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。
  • 通俗的理解,解构:左边一种结构,右边一种结构

2.2 解构赋值的分类

  • 数组解构赋值:左,右数组
    <span style="color:#000000">let a,b,rest;
    [a,b]=[1,2];
    [a,b,...rest]=[1,2,3,4,5,6];
    结果:a=1,b=2
         a=1,b=2,rest=[3, 4, 5, 6]
    </span>
  • 对象解构赋值:左,右对象
    <span style="color:#000000">let a,b;
    ({a,b}={a:2,b:0})
    
    结果:a=2,b=0;</span>
  • 字符串解构赋值:左数组,右字符串
  • 布尔值解构赋值:属于对象解构的一种
  • 函数参数解构赋值

2.3 默认值

  • 数组:
    <span style="color:#000000">[a,b,c=3]=[1,2];
    [a,b,c]=[1,2];
    
    
    结果:a=1,b=2,c=3;
         a=1,b=2,c=undefined;
    </span>
  • 对象
    <span style="color:#000000">let {a=10,b=2,c=8}={a:4}
    
    结果:a=4,b=2,c=8;</span>

2.4 使用场景

(1)变量值的交换

  • 数组
let a=1;
let b=2;
[a,b]=[b,a];

结果: a=2,b=1
  • 对象:
    let a={q:22,p:false};
    let {p,q}=a;
    
    结果:p=22,q=false;

(2)取函数中的变量值,可以选择性的取值

  • 数组:
    functionfoo(){
       return[1,2,3,4];
    }
    leta,b;
    [a,b]=foo();
    console.log(a,b);--a=1,b=2
    [a,,,b]=foo();
    console.log(a,b);--a=1,b=4
    [a,…b]=foo();
    console.log(a,b);--a=1,b=[2,3,4]
    
  • 数组与对象的联合使用--主要应对取后台数据相对应的值
let metaData={
    title:'metaData',
    test:[{
        title:'test',
        desc:'description';
    }]
}
{title:esTitle,test:[{title:testTitle}]}=metaData;

结果:esTitle=metaData,testTitle=test;

3 ES6之正则扩展

3.1 正则新增加特性

  • 构造函数的变化

  • 正则方法的扩展

  • u修饰符

  • y修饰符

  • s修饰符

3.2 表达方式的不同

  • es5的表示方法:
    let regexp1=newRegExp('xyz','i');
    let regexp2=newRegExp(/xyz/i);
    console.log(regexp1.test('xyz123'),regexp2.test('xyz123'));
  • es6的表示方法:es6中允许有第二个参数,但是第二个参数(i)会覆盖第一个参数(ig)

    • flags是es6新增加的,用来获取正则修饰符
      let regexp3=new RegExp(/xyz/ig,'i');
      console.log(regexp3.flags);//i
      

3.3 修饰符 

  • y修饰符与g修饰符

    • 都是全局匹配

    • g修饰符是从上一次匹配的位置的下一个位置开始匹配,直到匹配到为止,不强调是从哪个位置匹配到的。

    • y修饰符是从上一次匹配的位置的下一个位置开始匹配,必须是紧跟着的下一个字符匹配成功,才能够匹配。其中,sticky用来判断是否开启了y修饰符,开启了返回true,未开启返回false

    • 注:exec()用于检索字符串中的正则表达式的匹配。
      let s ='bbb_bb_b';
      let a =/b+/g;
      let b =/b+/y;
      console.log('one',a.exec(s),b.exec(s));//one ["bbb", index: 0, 
      input: "bbb_bb_b"]["bbb", index: 0, input: "bbb_bb_b"]
      
      console.log('two',a.exec(s),b.exec(s));//two ["bb", index: 4, input: "bbb_bb_b"]null
      
      console.log(a.sticky,b.sticky);//false true
  • u修饰符

    • 用于unicode编码
      console.log('u-1',/^\uD83D/.test('\uD83D\uDC2A'));//u-1 true
      
      console.log('u-2',/^\uD83D/u.test('\uD83D\uDC2A'));u-2 false
    • 注:没有u的会把\uD83D\uDC2A当作是2个字节,有u的会把\uD83D\uDC2A当作是1个字节

  • 点(.)

    • 处理的正则表达式中大于等于一个字符,则必须要加上/u;

    • es5中可以匹配所有字符,es6中只能匹配小于两个的所有字符,es6中并不是匹配所有字符

    • 回车符,换行符,行分隔符,段分隔符也是不能识别的

  • s修饰符--还未实现

4 ES6之字符串扩展(重要)

4.1 字符串新增特性:

  • Unicode表示法
  • 遍历接口
  • 模板字符串
  • 新增方法

4.2 Unicode表示法

  • 字符的编码大于0XFFFF的时候,unicode编码要用{}包起来
  • charAt()方法可返回指定位置的字符
  • charCodeAt()方法可返回指定位置的字符的 Unicode 编码。这个返回值是 0 - 65535 之间的整数。
  • 方法charCodeAt() 与 charAt() 方法执行的操作相似,只不过前者返回的是位于指定位置的字符的编码,而后者返回的是字符子串.
    console.log('a',"\u0061");//a a
    console.log('s','\u20BB7');// s ₻7(被分解成了两个字符)
    console.log('s','\u{20BB7}');//1 
    

4.3 遍历接口

  • for ... of 循环遍历字符串
    let text = 'abc'
    for (let i of text) {
      console.log(i);
    }
    

4.4 模板字符串

  • 传统的JavaScript语言,输出摸板通常:
    $("#result").append(
      "There are <b>" + basket.count + "</b> " +
      "items in your basket, " +
      "<em>" + basket.onSale +
      "</em> are on sale!"
    );
  • ES6的模板字符串:
  • $("#result").append(`
      There are <b>${basket.count}</b> items
       in your basket, <em>${basket.onSale}</em>
      are on sale!
    `);
    其中,将字符串写在``中,即`字符串`。${}表示变量。不需要像js中表示变量要用+连接字符串和变量。

4.5 新增方法(10种)

      子字符串的识别

  1. 【includes()】

      该方法在给定文本存在于字符串中的任意位置时会返回 true ,否则返回false

  2.  

    【startsWith()】

      该方法在给定文本出现在字符串起始处时返回 true ,否则返回 false

  3.  

    【endsWith()】

      该方法在给定文本出现在字符串结尾处时返回 true ,否则返回 false 

    var s = 'Hello world!';
    
    s.startsWith('Hello') // true
    s.endsWith('!') // true
    s.includes('o') // true
    
    //三个方法都支持第二个参数,表示开始搜索的位置
    s.startsWith('world', 6) // true
    s.endsWith('Hello', 5) // true  向前搜索
    s.includes('Hello', 6) // false
    字符串的重复

     

  4.  

    【repeat()】

      ES6为字符串添加了一个 repeat() 方法,它接受一个参数作为字符串的重复次数,返回一个将初始字符串重复指定次数的新字符串

    'x'.repeat(3) // "xxx"
    'hello'.repeat(2) // "hellohello"
    'na'.repeat(0) // ""
    补全长度

     

  5.  

    【padStart()】

      头部补全

 

【padEnd()】

  尾部补全

'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'

'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'

//原字符串的长度,等于或大于指定的最小长度,则返回原字符串
'xxx'.padStart(2, 'ab') // 'xxx'
'xxx'.padEnd(2, 'ab') // 'xxx'

//补全的字符串与原字符串,两者的长度之和超过了指定的最小长度,则会截去超出位数的补全字符串
'abc'.padStart(10, '0123456789')// '0123456abc'

//省略第二个参数,则会用空格补全长度
'x'.padStart(4) // '   x'
'x'.padEnd(4) // 'x   '

5 ES6之数值扩展

5.1 数值处理新增特性

1、新增方法

2、方法调整

5.2进制

  • 不区分大小写

console.log('b',0b111110111);//二进制503
console.log('B',0B111110111);//二进制503
console.log(0o767);//八进制503

 

5.3 判断是否有限

  • Number.isFinite()

console.log('15',Number.isFinite(15));--true
console.log('NaN',Number.isFinite(NaN));--false
console.log('1/0',Number.isFinite('true'/0));--false

 

5.4 判断是否是数

  • Number.isNaN()

console.log('NaN',Number.isNaN(NaN));--true
console.log('0',Number.isNaN(0));--false

 

5.5 判断是否是整数

  • Number.isInteger(必须是一个数)--重要

console.log('25',Number.isInteger(25));--true
console.log('25.0',Number.isInteger(25.0));--true
console.log('25.1',Number.isInteger(25.1));--false
console.log('25',Number.isInteger('25'));--false

 

5.6 支持的最大、最小限

  • Number.MAX_SAFE_INTEGER,Number.MIN_SAFE_INTEGER

console.log(Number.MAX_SAFE_INTEGER,Number.MIN_SAFE_INTEGER);----9007199254740991 -9007199254740991

 

5.7 判断一个数是不是安全的数--是否在最大、最小范围之内

  • Number.isSafeInteger(必须是一个数字)

console.log('10',Number.isSafeInteger(10));--true
console.log('a',Number.isSafeInteger(a);//false

 

5.8 返回带小数的整数

  • Math.trunc()向下取整

console.log(4.1,Math.trunc(4.1));--4
console.log(4.9,Math.trunc(4.9));--4

 

5.9 判断0,正数,负数

  • Math.sign(必须是数字)

console.log('0',Math.sign(0));-- 0
console.log('5',Math.sign(5));-- 1
console.log('-5',Math.sign(-5));-- -1
console.log('foo',Math.sign('foo'));-- NaN

 

注:0就返回0,正数返回1,负数返回-1,字符串返回NaN

5.10 立方根的计算--Math.cbrt( )

5.11 三角函数

5.12 对数

6 ES6之数组扩展(重要)

ES6对于数组又扩展了很多方法,包括静态方法和原型链上添加的方法,让我们可以更方便的操作数组。

  • Array.from()
    • Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。

      let arrayLike = {  
          '0': 'a',  
          '1': 'b',  
          '2': 'c',  
          length: 3  
      };  
        
      // ES5的写法  
      var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']  
        
      // ES6的写法  
      let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']  
      
    • 如果参数是一个数组,那么会返回一个一模一样的新数组(浅拷贝),而且这个方法会将数组空位转换为undefined;换句话说,会忽略空位。
      let arr = Array.from([10, , 30]);
      console.log(arr); //[10, undefined, 30]
      
    • 还有一个额外的参数 ,用于对元素进行处理 ,类似于ES5的数组方法map。
      let arrLike = {
          0: 10,
          1: 20,
          2: 30,
          length: 3
      }
      let arr = Array.from(arrLike,function(x){
          return x*x;
      });
      console.log(arr); //[100, 400, 900]
  • Array.of()
    • 用于将一组值转换为数组,它弥补了使用new Array()构造数组的奇怪行为,比如说填入一个参数,只填写一个参数就会构建一个长度为3的稀疏数组

      let arr = new Array(3); //构建了一个稀疏数组
      
    • Array.of()可以很好的解决这个问题
      let arr=Array.of(3,4,5,6,7);
      console.log('arr=',arr);//arr=[3, 4, 5, 6, 7]
      let empty=Array.of();
      console.log('empty=',empty);//empty= []
  • 内部拷贝CopyWithin()

    • 这是一个原型方法,作用是把这个数组的一部分元素复制到其他位置 ,这会覆盖原有的元素 ,返回当前数组 .换句话说,这个方法会修改原数组.
    • 第一个参数代表从这个位置开始替换
      后两个参数代表要拷贝的起始位置和结束为止
      同样不包含结束元素,左闭右开
      可以使用负值代表倒数
      let arr = [1, 2, 3, 4, 5, 6, 7, 8];
      arr.copyWithin(1, 5, 8);
      console.log(arr); //[1, 6, 7, 8, 5, 6, 7, 8]
  • 查找find()或findIndex()

    • 这两个原型方法都有一个回调函数作为参数 (回调函数的参数依次为元素、索引、数组引用,与ES5数组方法相同)
      find()会返回第一个满足条件的元素 ,findIndex()会返回第一个满足条件的索引 ,没有找到都会返回-1 ,这里满足条件意思就是函数参数返回值为true
      console.log([1,2,3,4,5,6].find(function(item){
      return item > 3;//4 (只返回第一个满足条件的内容)
      }))
      console.log([1,2,3,4,5,6].findIndex(function(item){
      return item>3;//3(返回第一个满足条件的下标)
      }))
      
      
  • 填充fill()

    • 用于填充数组 ,会修改调用它的数组
      console.log('fill-1',[1,'a',undefined].fill(7));//7 7 7
      console.log('fill,pos',[1,'a',undefined].fill(7,1,3));//fill,pos [1, 7, 7]
      
      
  • 包含include()

    • 用于检测数组是否含有某个特定值,返回布尔值。这个方法甚至连NaN都可以检测到。
      console.log('number',[1,2,NaN].includes(1));//true
      
      
    • 但是我们ES5中的indexOf()是不可以的
      let arr = [1, 'a', true, null, NaN];
      console.log(arr.indexOf(1));             //0
      console.log(arr.indexOf('a'));           //1
      console.log(arr.indexOf(true));          //2
      console.log(arr.indexOf(null));          //3
      console.log(arr.indexOf(NaN));           //-1
      由此可见indexOf()内部是用严格等于判断的 ,我们可以这样来判断NaN
      let demo = NaN;
      console.log(demo + '' === 'NaN'); //true

这里注意isNaN()不仅仅是非数返回true ,不是数字都会返回true

  • 数组迭代entries()/keys()/values()

    • 它们用于迭代数组,均返回一个迭代器对象 ,配合for-of循环可以迭代数组
      for(let index of ['1','c','ks'].keys()){
      console.log('keys',index);//012
      }
      for(let value of ['1','c','ks'].values()){
      console.log('values',value);//1 c ks
      }
      for(let [index,value] of ['1','c','ks'].entries()){
      console.log('index->values',index,value);
      }//
      index->values 0 1
      index->values 1 c
      index->values 2ks
      
      
    • 不过for-of就是用于遍历数组的(用for-in遍历对象),如果全遍历使用for-of更加方便

7 ES6之函数扩展(非常重要)

 

  • 箭头函数

    • 基本用法 :ES6允许使用“箭头”(=>)定义函数,即 函数名 = (函数参数) => {函数返回值}
//不是箭头函数
var f = function (v){
    return v;  
};                                                                                              //箭头函数(有参数)
var f=(v)=>{    
    return v;
}
//箭头函数(无参数)
var f=()=>{
    return 5;
}
    • 箭头函数有几个使用注意点。
      (1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
      (2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
      (3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。
      (4)不可以使用yield命令,因此箭头函数不能用作Generator函数。
      上面四点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的。
      function foo() {  
        setTimeout(() => {  
          console.log('id:', this.id);  
        }, 100);  
      }  
        
      var id = 21;  
        
      foo.call({ id: 42 });  
      // id: 42  
      上面代码中,setTimeout的参数是一个箭头函数,这个箭头函数的定义生效是在foo函数生成时,而它的真正执行要等到100毫秒后。如果是普通函数,执行时this应该指向全局对象window,这时应该输出21。但是,箭头函数导致this总是指向函数定义生效时所在的对象(本例是{id: 42}),所以输出的是42。
      箭头函数可以让setTimeout里面的this,绑定定义时所在的作用域,而不是指向运行时所在的作用域。
  • 参数默认值

    •  //如果使用let或者const在函数内部再次声明会报错
              function Point(x=0,y=0){
                  this.x=x;
                  this.y=y;
              }
              var p=new Point()
              console.log(p.x,p.y);//0,0
      
              //函数不能有同名参数,否则就报错
               function Point2(x=0,x,y=0){
                  //Duplicate parameter name not allowed in this context
                  this.x=x;
                  this.y=y;
               }
      
              //如果参数默认值是包含变量的表达式,那么参数就不能传值,需要修改变量的值来改变参数值
              let o=99;
              function foo(p=o+1){
                  console.log(p)
              }
              foo();//100
      
              o = 100;
              foo() // 101
      
      
  • 参数作用域

    • //当函数的参数中有设置默认值的参数时,包含参数的圆括号就会形成一个作用域
              let x=1;
              //参数y有默认值x,所以圆括号就形成一个临时作用域,y=变量x,变量x=参数x,参数通过传值得到 2 ,而取不到全局的x值
              function f(x,y=x){
                  console.log(y);
              }
              f(2)//2
      
      ----------------------------------------------------------------------------------
              let x=1;
              //参数y有默认值x,所以圆括号就形成一个临时作用域,y=变量x,由于本作用域中没有x变量,所以向全局中取得x的值
              function f(y=x){
                  console.log(y);
              }
              f()//1
      --------------------------------------------------------------------------------
              //下面这样写 也会报错,变量必须先声明再调用
              var x = 1;
              function foo(x = x) {
               ...
              }
      
              foo() // ReferenceError: x is not defined
      
      
  • rest参数

    • 把一系列的参数转换成数值,rest参数之后不能有其他的参数(值转换成数组)
    • rest参数 与之前的变量结构赋值的rest变量是一个东西;
      需要注意的是,rest参数会被length属性忽略,就是前面说的函数length;
      rest参数与参数默认值一样 必须放到参数的最后一个
    • 参数的形式: …变量名

      rest搭配的变量是一个数组,就可以不再使用arguments对象了

      function test3(...arg){
         for(let v of arg){
           console.log('rest参数',v);
         }
      }
      test3(1,2,3,4,'a');
      //
      rest参数1
      rest参数2
      rest参数3
       rest参数 4
       rest参数 a
      
      

       

  • 扩展运算符

    • 是rest参数的逆运算(将数组转换为用逗号分隔的参数列表)
    • 形式: …
    • console.log(...[1,2,4]);//1 2 4转换成一个个的值
      console.log('a',...[1,2,4]);//a 1 2 4
      
      
  • 尾调用

    • 尾调用(Tail Call)是函数式编程的一个重要概念,本身非常简单,一句话就能说清楚,就是指某个函数的最后一步是调用另一个函数。
      function f(x){  
        return g(x);  
      }  
      上面代码中,函数f的最后一步是调用函数g,这就叫尾调用。
      以下三种情况,都不属于尾调用。
      // 情况一  
      function f(x){  
        let y = g(x);  
        return y;  
      }  
        
      // 情况二  
      function f(x){  
        return g(x) + 1;  
      }  
        
      // 情况三  
      function f(x){  
        g(x);  
      }  
      上面代码中,情况一是调用函数g之后,还有赋值操作,所以不属于尾调用,即使语义完全一样。情况二也属于调用后还有操作,即使写在一行内。情况三等同于下面的代码。
      function f(x){  
        g(x);  
        return undefined;  
      }  
      尾调用不一定出现在函数尾部,只要是最后一步操作即可。
      function f(x) {  
        if (x > 0) {  
          return m(x)  
        }  
        return n(x);  
      }  
      上面代码中,函数m和n都属于尾调用,因为它们都是函数f的最后一步操作。
       
  • 尾调用优化

    • 尾调用之所以与其他调用不同,就在于它的特殊的调用位置。
      我们知道,函数调用会在内存形成一个“调用记录”,又称“调用帧”(call frame),保存调用位置和内部变量等信息。如果在函数A的内部调用函数B,那么在A的调用帧上方,还会形成一个B的调用帧。等到B运行结束,将结果返回到A,B的调用帧才会消失。如果函数B内部还调用函数C,那就还有一个C的调用帧,以此类推。所有的调用帧,就形成一个“调用栈”(call stack)。
      尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用帧,取代外层函数的调用帧就可以了。
      function f() {  
        let m = 1;  
        let n = 2;  
        return g(m + n);  
      }  
      f();  
        
      // 等同于  
      function f() {  
        return g(3);  
      }  
      f();  
        
      // 等同于  
      g(3);  
      上面代码中,如果函数g不是尾调用,函数f就需要保存内部变量m和n的值、g的调用位置等信息。但由于调用g之后,函数f就结束了,所以执行到最后一步,完全可以删除 f(x) 的调用帧,只保留 g(3) 的调用帧。
      这就叫做“尾调用优化”(Tail call optimization),即只保留内层函数的调用帧。如果所有函数都是尾调用,那么完全可以做到每次执行时,调用帧只有一项,这将大大节省内存。这就是“尾调用优化”的意义。
      注意,只有不再用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧,否则就无法进行“尾调用优化”。
      function addOne(a){  
        var one = 1;  
        function inner(b){  
          return b + one;  
        }  
        return inner(a);  
      }  
      
      
      
  • 尾递归

    • 函数调用自身,称为递归。如果尾调用自身,就称为尾递归。
      递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生“栈溢出”错误(stack overflow)。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。
      function factorial(n) {  
        if (n === 1) return 1;  
        return n * factorial(n - 1);  
      }  
        
      factorial(5) // 120  
      上面代码是一个阶乘函数,计算n的阶乘,最多需要保存n个调用记录,复杂度 O(n) 。
      如果改写成尾递归,只保留一个调用记录,复杂度 O(1) 。
      function factorial(n, total) {  
        if (n === 1) return total;  
        return factorial(n - 1, n * total);  
      }  
        
      factorial(5, 1) // 120  
      还有一个比较著名的例子,就是计算fibonacci 数列,也能充分说明尾递归优化的重要性
      如果是非尾递归的fibonacci 递归方法
      function Fibonacci (n) {  
        if ( n <= 1 ) {return 1};  
        
        return Fibonacci(n - 1) + Fibonacci(n - 2);  
      }  
        
      Fibonacci(10); // 89  
      // Fibonacci(100)  
      // Fibonacci(500)  
      // 堆栈溢出了  
      如果我们使用尾递归优化过的fibonacci 递归算法
      function Fibonacci2 (n , ac1 = 1 , ac2 = 1) {  
        if( n <= 1 ) {return ac2};  
        
        return Fibonacci2 (n - 1, ac2, ac1 + ac2);  
      }  
        
      Fibonacci2(100) // 573147844013817200000  
      Fibonacci2(1000) // 7.0330367711422765e+208  
      Fibonacci2(10000) // Infinity  
      由此可见,“尾调用优化”对递归操作意义重大,所以一些函数式编程语言将其写入了语言规格。ES6也是如此,第一次明确规定,所有ECMAScript的实现,都必须部署“尾调用优化”。这就是说,在ES6中,只要使用尾递归,就不会发生栈溢出,相对节省内存。
  • 尾递归优化

    • 尾递归优化只在严格模式下生效,那么正常模式下,或者那些不支持该功能的环境中,有没有办法也使用尾递归优化呢?回答是可以的,就是自己实现尾递归优化。
      它的原理非常简单。尾递归之所以需要优化,原因是调用栈太多,造成溢出,那么只要减少调用栈,就不会溢出。怎么做可以减少调用栈呢?就是采用“循环”换掉“递归”。

 

注意:尾调用和尾递归是参考别处。

8 ES6之对象扩展

函数新增特性:

简洁表示法

(1)普通对象

let a=1;

let b=2;

let es5={

a:a,

b:b

};

let es6={

a,

b

};

console.log(es5,es6);

Object{a: 1, b: 2} Object {a: 1, b:2}结果一样

(2)带有函数的对象

let es5_methods={

hello:function(){

console.log('hello');

}

};

 

let es6_methods={

hello(){

console.log('hello');

}

};

console.log(es5_methods.hello(),es6_methods.hello());--hello

 

属性表示式

let a='b';

let es5_obj={

a:'c',

b:'c'

};

console.log(es5_obj);--Object {a: "c", b:"c"}

let es6_obj={

[a]:'c'

};

console.log(es6_obj);--Object {b: "c"}

 

扩展运算符

--import'babel-polyfill'支持性不好

let {a,b,...c}={a:'test',b:'kill',c:'ddd',d:"ccc"};

Object新增方法

判断是否相等  类似于===

Object.is('字符串','字符串');

Object.is('数组','数组');

拷贝:合并两个对象--浅拷贝,只拷贝自身对象的属性,不拷贝来自继承,枚举的对象

Object.assign({原始对象},{要拷贝的对象});

9 ES6之Symbol

Symbol的概念

提供一个独一无二的值,是一种数据类型

//声明

Let a1=Symbol();

Let a2=Symbol();

console.log(a1===a2);--false永不相等

//Symbol.for()与Symbol()一样都是声明一个值,但是Symbol.for(key)会检查之前有没有声明过key,如果声明了则返回声明的值,之前没有声明过,则生成一个独一无二的值

Let a3=Symbol.for('a3');

Let a4=Symbol.for('a3');

console.log(a3===a4);---true

symbol的作用

(1)

let a1=Symbol.for('abc');

let obj={

[a1]:'123',

'abc':345,

'c':456

};

console.log('obj',obj);---obj Object {abc: 345, c: 456,Symbol(abc): "123"}

//利用for of遍历是取不到symbol()声明的值,只能取到对象里面的值

for(let [key,value] of Object.entries(obj)){

console.log('letof',key,value);--let of abc 345 , let of c 456

}

 

//只能得到Symbol()声明的值,使用Object.getOwnPropertySymbols(obj),得到一个数组

Object.getOwnPropertySymbols(obj).forEach(function(item){

console.log(obj[item]);---123

});

//利用Reflect.ownKeys(obj)可以取到obj中所有的值,返回值是一个数组

Reflect.ownKeys(obj).forEach(function(item){

console.log('ownKeys',item, obj[item]);

})

返回值:

ownKeys abc 345

ownKeys c 456

ownKeys Symbol(abc)123

10 ES6之Set-map

Set的用法(与数组相似,但是里面的元素是不能重复的)

//声明 Set()--声明的时候未添加默认值

let list= new Set();

//add()给Set()添加元素

list.add(5);

list.add(7);

list.add(7);---不能添加重复的元素,不会生效,但是不会报错

//size计算Set()的大小

console.log('size',list.size);-- 2

//声明 Set()--声明的时候添加默认值

let arr=[1,2,3,4,5];

let list= new Set(arr);

console.log('size',list.size);--5

Set可以用用于去重

let arr=[1,2,3,4,5,2,4];

let list = new Set(arr);

console.log('unique',list);--unique Set(5) {1, 2, 3, 4, 5}

  • add():添加元素

  • delete():删除特定元素

  • clear() :删除全部元素,即清空

  • has():是否包含某个元素

Set()的遍历

let arr=['add','delete','clear','has'];

let list=new Set(arr);

(1)for(let key of list.keys()){

console.log('keys',key);--add delete clear has

}

(2)for(let value of list.values()){

console.log('values',value);--add delete clear has

}

(3)for(let [key,value] oflist.entries()){

console.log('entries',key,value);--add add delete delete clear clearhas has

}

(4)list.forEach(function(item){

console.log(item);--add delete clear has

})

 

WeakSet的用法

--不能进行遍历

//WeakSet与Set支持的数据类型不一样(他的元素只能是对象)

//WeakSet的引用只能是弱引用

let weakList=newWeakSet();

let arg={};--只能是对象

weakList.add(arg);

console.log('weakList',weakList);

  • add():添加元素

  • delete():删除特定元素

  • clear() :删除全部元素,即清空

  • has():是否包含某个元素

Map的用法

(与对象object相似,都是键/值对,但object里面的key是字符串,但Map里面的key可以是任意的数据类型)

  • 第一种定义方式:

let map=newMap();

let arr=['123'];

map.set(arr,456);---添加值,key,value

console.log('map',map,map.get(arr));--get()获取值map Map(1) {["123"] => 456}456

  • 第二种定义方式:

let map=new Map([['a',123],['b',456]]);

console.log('mapargs',map);--map args Map(2) {"a"=> 123, "b" => 456}

Map对象的属性值:

  • Size:获取大小

  • Get()获取元素

  • delete():删除特定元素

  • clear() :删除全部元素,即清空

遍历于Set一样

WeakMap的用法--也不能遍历,key必须是对象

let weakmap=newWeakMap();

11 ES6之Proxy和Reflect

Proxy和Reflect:

Proxy和Reflect的概念---方法一样

Proxy:代理,扮演代理的作用---连接了用户和最真实的对象中间的一个层

Reflect:反射的作用---反射object

Proxy和Reflect的适用场景

Proxy的用法:

letobj={

time:'2017-7-21',

name:'net',

_r:123

};

letmonitor=newProxy(obj,{

//拦截对象属性的读取--读get()

get(target,key){//target代表obj对象

returntarget[key].replace('2017','2018');

},

//拦截对象,设置属性--写set()

set(target,key,value){

if(key==='name'){

returntarget[key]=value;

}else{

returntarget[key];

}

},

//拦截keyinobject操作

has(target,key){

if(key==='name'){

returntarget[key];

}else{

return false;

}

},

//拦截不需要的属性--拦截delete

deleteProperty(target,key){

if(key.indexOf('_')>-1){//检索到下划线,不需要

deletetarget[key];

return true;

}else{

returntarget[key];

}

},

//拦截object.keys,object.getOwnPropertySymbols,object.getOwnPropertyNames

ownKeys(target){

returnObject.keys(target).filter(item=>item!='time');

}

 

});

console.log('get',monitor.time);

 

monitor.time='2018-7-22';//在set()没有定义time,不能改变time内容

monitor.name='network';//在set()里面设置了name,可以改变内容

console.log('set',monitor.time,monitor.name,monitor);

console.log('has','name'inmonitor,'time'inmonitor);

deletemonitor.time;//time是符合规范的,不会删除掉

deletemonitor._r;//_r不符合规范,会被删掉

console.log('deleteProperty',monitor);

console.log('ownKeys',Object.keys(monitor));

 

reflect的用法:---与Proxy的用法一致,只是表示方法不同

letobj={

time:'2017-7-21',

name:'net',

_r:123

};

console.log(Reflect.get(obj,'time'));

Reflect.set(obj,'name','network');

console.info(obj);

console.log(Reflect.has(obj,'name'));

实际例子:

//用户代理

functionvalidator(target,validator){//target是对Person的代理

return newProxy(target,{

_validator:validator,

set(target,key,value,proxy){

if(target.hasOwnProperty(key)){

letva=this._validator[key];

if(!!va(value)){

returnReflect.set(target,key,value,proxy);

}else{

throwError(`不能设置${key}${value}`)

}

}else{

throwError(`${key}不存在`);

}

}

})

}

//限制属性的条件,与业务分离validator

constpersonValidators={

name(val){

return typeofval==='string';

},

age(val){

return typeofval==='number'&&val>18

}

};

classPerson{

constructor(name,age){

this.name=name;

this.age=age;

returnvalidator(this,personValidators);

}

}

constperson=newPerson('KEKE',20);

console.log(person);

person.name=48;//报错不能设置name到48

 

12 ES6之类和对象

类的概念

  • 基本语法

//类的基本定义和生成实例

//类的定义

classParent{

constructor(name='keke'){

this.name=name;

}

}

//生成实例

letv_parent1=newParent();//默认name:keke

letv_parent2=newParent('nono');//name:nono

 

  • 类的继承

//继承

classParent{

constructor(name='keke'){

this.name=name;

}

}

classChildextendsParent{

 

}

letchild=newChild();//name:keke

//继承传递参数

classParent{

constructor(name='keke'){

this.name=name;

}

}

classChildextendsParent{

constructor(name='child'){

super(name);//super(参数):参数为空,则默认使用父类的值;参数不为空,则传递自己的参数

this.type='child';

}

}

letchild=newChild();//name:child---存在super(name)

letchild=newChild();//name:keke---不存在super(name)

 

  • 静态方法

  •        --通过类去调用,而不是通过类的实例去调用

//静态方法

classParent{

constructor(name='keke'){

this.name=name;

}

//静态方法的定义

statictell(){

console.log('tell');

}

}

Parent.tell();//tell

  • 静态属性

//静态属性

classParent{

constructor(name='keke'){

this.name=name;

}

statictell(){

console.log('tell');

}

}

Parent.type='text';//设置静态属性

console.log(Parent.type);//text

 

  • Getter和setter属性

//getter,setter

classParent{

constructor(name='keke'){

this.name=name;

}

getlongName(){

return'mk'+this.name;

}

setlongName(value){

this.name=value;

}

}

letv=newParent();

console.log(v.longName);//mkkeke

v.longName='hello';//赋值相当于setter

console.log(v.longName);//mkhello

13 ES6之Promise

(1)Promise是异步操作的一种解决方案

(2)什么是异步?

先执行什么,后执行什么

(3)Promise的作用

(4)Promise的基本用法

//基本定义的调用

letajax=function(callback){

console.log('执行');

setTimeout(function(){

callback&&callback.call();

},1000);

};

ajax(function(){

console.log('timeout1');

})

//Promise的调用--一次调用

letajax=function(){

console.log('执行');

Return newPromise(function(resolve,reject){

setTimeout(function(){

resolve();//

},1000);

})

};

ajax().then(function(){

console.log('promise','timeout2');

})

//Promise的调用--连续调用

letajax=function(){

console.log('执行3-1');

Return newPromise(function(resolve,reject){

setTimeout(function(){

resolve();

},1000);

})

};

ajax()

.then(function(){

return newPromise(function(resolve,reject){

console.log('执行3-2');

setTimeout(function(){

resolve();

},2000);

})

})

.then(function(){

console.log('promise','执行3-3');

})

 

//捕获异常错误

letajax=function(num){

console.log('执行4');

return newPromise(function(resolve,reject){

if(num>5){

resolve();

}else{

throw newError('出错了');

}

})

};

ajax(6).then(function(){

console.log('log',6);

}).catch(function(err){

console.log('catch',err);

});

ajax(3).then(function(){

console.log('log',3);

}).catch(function(err){

console.log('catch',err);//捕获到错误

})

 

//所有图片加载完再加载页面

 

//加载图片

functionloadImg(src){

return newPromise((resolve,reject)=>{

letimg=document.createElement('img');

img.src=src;

//图片加载成功

img.οnlοad=function(){

resolve();

};

//图片加载失败

img.οnerrοr=function(){

reject(err);

}

})

}

//将图片加载到页面上

functionshowImgs(imgs){

imgs.forEach(function(img){

document.body.appendChild(img);

})

}

Promise.all([

loadImg('图片地址'),

loadImg('图片地址'),

loadImg('图片地址')

]).then(showImgs)

 

Promise.all():只有在all()里面的内容发生改变的时候且全部加载完成,就会触发Promise.all()这个实例

Promise.race():只要race()里面的内容有一个加载完成,就可以触发Promise.race()实例

//只要有一张图片加载完就加载页面

//加载图片

functionloadImg(src){

return newPromise((resolve,reject)=>{

letimg=document.createElement('img');

img.src=src;

//图片加载成功

img.οnlοad=function(){

resolve();

};

//图片加载失败

img.οnerrοr=function(){

reject(err);

}

})

}

functionshowImgs(imgs){

imgs.forEach(function(img){

letp=document.createElement('p');

p.appendChild(img);

document.body.appendChild(p);

})

}

Promise.race([

loadImg('图片地址'),

loadImg('图片地址'),

loadImg('图片地址')

]).then(showImgs);

14 ES6之Iterator接口和for…of循环

(1)什么是Iterator接口

(2)Iterator的基本用法

letarr=['hello','world'];

letmap=arr[Symbol.iterator]();

console.log(map.next());

console.log(map.next());

console.log(map.next());

 

(2)部署接口:有颜色字体标志的是必须要有的(重要)

 

letobj={

start:[1,3,2],

end:[7,9,8],

[Symbol.iterator](){

letself=this;

letindex=0;

letarr=self.start.concat(self.end);

letlen=arr.length;

return{

next(){

if(index<len){

return{

value:arr[index++],

done:false

}

}else{

return{

value:arr[index++],

done:true

}

}

}

}

}

};

 

for(letkeyofobj){

console.log(key);//1 3 2 7 9 8

}

 

for… of

letarr=['hello','world'];

for(letvalueofarr){

console.log(value);//hello world

}

15 ES6之Generator

(1)基本概念

异步编程的一种解决方案

(a)genertor的基本定义

lettell=function*(){

yield'a';

yield'b';

return'c';

};

letk=tell();

console.log(k.next());

console.log(k.next());

console.log(k.next());

console.log(k.next());

 

(2)generator与Iterator接口的关系

letobj={};

//创建一个接口

obj[Symbol.iterator]=function*(){

yield1;

yield2;

yield3;

};

for(letvalueofobj){

console.log(value);

}

(3)状态机

letstate=function*(){

while(1){

yield'a';

yield'b';

yield'c';

}

};

letstates=state();

console.log(states.next());//a

console.log(states.next());//b

console.log(states.next());//c

console.log(states.next());//a

console.log(states.next());//b

(4)语法糖:

  • async ---实现方式与上面一样

letstate= async function(){

while(1){

await'a';

await'b';

await'c';

}

};

letstates=state();

console.log(states.next());//a

console.log(states.next());//b

console.log(states.next());//c

console.log(states.next());//a

console.log(states.next());//b

 

 

//抽奖

letdraw=function(count){

//具体抽奖逻辑

console.info(`剩余${count}次`)

};

letresidue=function*(count){

while(count>0){

count--;

yielddraw(count);

   }

};

letstar=residue(5);

letbtn=document.createElement('button');

btn.id='start';

btn.textContent='抽奖';

document.body.appendChild(btn);

document.getElementById('start').addEventListener('click',function(){

star.next();

},false);

 

 

(5)长轮询:

客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。

letajax=function*(){

yield newPromise(function(resolve,reject){

setTimeout(function(){

resolve({code:0});

},200);

})

};

letpull=function(){

letgenerator=ajax();

letstep=generator.next();

step.value.then(function(d){

if(d.code!=0){//长轮询的查询

setTimeout(function(){

console.log('wait');

pull();

},1000);

}else{

console.log(d);

}

})

};

pull();

(6)next函数的用法

(7)Yield*的语法

16 ES6之Decorator--修饰器

(1)基本概念

是一个函数,修改行为,修改类的行为,只在类的范畴内有用

 

(2)基本用法

//定义修饰器

第一种用法:在类的里面使用

letreadonly=function(target,name,descriptor){

descriptor.writable=false;

returndescriptor;

};

classTest{

@readonly//引入修饰器,修改time()的行为

time(){

return'2017-07-21';

}

}

lettest=newTest();

test.time=function(){//修饰器设置的只读属性,不能进行修改会报错

console.log('resettime')

};

console.log(test.time());

 

第二种用法:在类的外面使用

lettypename=function(target,name,descriptor){

target.myname='hello';

};

@typename

classTest{

 

}

console.log(Test.myname);

扩展:

第三方库修饰器的js库:core-decorators; npm install core-decorators

就不用自己手动的写readonly,typename等,只需要import引入就可以使用

 

//埋点,做日志统计

letlog=(type)=>{

return function(target,name,descriptor){

letsrc_method=descriptor.value;

descriptor.value=(...arg)=>{

src_method.apply(target,arg);

console.info(`log${type}`);//实现埋点,真实的业务中直接换成一个接口就可以了

}

}

};

classAD{

@log('show')

show(){

console.log('adisshow');

};

@log('click')

click(){

console.log('adisclick');

}

}

letad=newAD();

ad.show();

ad.click();

17 ES6之模块化

(1)基本概念

(2)Es6的模块化语法

  • 模块的引入import

  • 模块的导出export

 

//导出变量,函数,类

第一种写法:--对应第(1)(2)种导出方法

export letA=123;

export functionTest(){

console.log('Test');

}

export classHello{

test(){

console.log('test');

}

}

第二种写法:--对应第(3)种导出方法----推荐使用

letA=123;

letTest=function(){

console.log('Test');

};

classHello{

test(){

console.log('test');

}

}

export default{

A,

Test,

Hello

}

 

//导入

(1)import{A,Test,Hello}from'./class/lesson17';//导入的内容逐个写,当内容很多的时候,不适用

(2)import*aslessonfrom'./class/lesson17';//全部一起导入,用的时候再针对性的取

          console.log(lesson.A);

(3)importlessonfrom'./class/lesson17';

          console.log(lesson.A);

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值