zoukankan      html  css  js  c++  java
  • 浅谈ES6

    ECMAScript6.0(简称ES6)是javaScript语言的下一代标准,已经在2015年6月正式发布了。它的目标,使得javaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言。各大浏览器的最新版本,随着时间的推移,支持度已经越来越高了,ES6的大部分特性都实现了。那么也就意味着低版本浏览器是不支持ES6的。

    本次我会从以下十点来做讲解

    一、let 和 const

    二、变量解构赋值

    三、字符串扩展

    四、数值扩展

    五、数组扩展

    六、函数扩展

    七、对象扩展

    八、Set 和 Map结构

    九、Generator 生成器函数

    十、class写法

    一、let 和 const

    1、let可以声明变量

    let name = '张三';
    console.log(name);//张三

    2、ES6新增块级作用域

      注:在ES6之前作用域分为:全局作用域、函数作用域

    {
        var name1 = '张三';
        let name2 = '李四';
        console.log(name2);//李四
    }
    console.log(name1);//张三
    console.log(name2);//报错

      上面代码在代码块之中,分别用let和var声明了两个变量。然后在代码块之外调用这两个变量,结果let声明的变量报错,var声明的变量返回了正确的值。而在代码块内let声明的变量返回了正确的值。这表明,let声明的变量只在它所在的代码块有效。

    3、let的使用场景

    var a =[];
    for(var i=0;i<10;i++){
        a[i] = function(){
            console.log(i);
        }
    }
    a[6]();//10

      上面的代码中,变量i是var声明的,在全局范围内都有效。所以每一次循环,新的i值都会覆盖旧值,导致最后输出的是最后一轮的i的值。

    var a = [];
    for(let i=0;i<10;i++){
        a[i]=function(){
            console.log(i);
        }
    }
    a[6]();//6

      上面代码中,变量i是let声明的,声明的变量仅在块级作用域内有效,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,最后输出的是6.

     4、不存在变量提升

    console.log(a);
    console.log(b);
    
    var a = 2;//undefined
    let b = 3;//报错

      上面代码中,变量a用var命令声明,会发生变量提升,即脚本运行时,变量a已经存在了,但是么有值,所以会输出undefined。变量b用let命令声明,不会发生变量提升。这表明在声明它之前,变量b是不存在的,这时如果用到它,就会抛出一个错误。

    5、暂时性死区

    let b = 3
    {
        b=4
    }
    console.log(b);//4

      当代码执行到代码块中时,b=4在代码块中是没有定义的,所以它会往外找定义的变量,找到let b=3;那么b=4就会覆盖b=3,所以最后 b=4

    let b=3
    {
        b=4;
        let b=5;
    }
    console.log(b);//报错

      上面代码中,存在全局变量b,但是块级作用域内又声明了一个局部变量b,导致后者绑定这个块级作用域,不会再向块区外面找,所以在let声明变量前,对b赋值就会报错。暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

      ES6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成看封闭作用域。凡是在声明之前就使用这些变量,就会报错。总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。在语法上,称为“暂时性死区”。

    6、不允许重复声明

    {
        let a = 10;
        let a = 8;
        console.log(a);//报错
    }

      let不允许在相同的作用域内重复声明同一个变量。

    7、顶层对象属性

    var a = 2;
    console.log(window.a);//2
    
    let b = 3;
    console.log(window.b);//undefined

      顶层对象,在浏览器环境指的是window对象,在Node指的是global对象。ES5之中,顶层对象的属性与全局变量是等价的。

      ES6规定,var命令和function命令声明的全局变量,依旧是顶层对象的属性;let命令、const命令、class命令声明的全局变量、不属于顶层对象属性。 所以上面代码中,全局变量b是由let声明 ,不是顶层对象,返回undefined。

    8、const

      上面介绍 let 有的属性,const 都有;唯一的区别是const声明的变量是不可以修改的,const声明一个只读的常量,一旦声明,常量的值就不能改变

    const a =2;
    a=3;
    console.log(a);//报错

      const特殊的场景

    const a ={name:'张三'};
    a.name = '李四';
    console.log(a);//李四

      上面代码中,常量a储存的是一个地址,这个地址指向一个对象,不可变的只是这个地址,即不能把a指向另一个地址,但对象本身是可变的

    二、变量解构赋值

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

    1、模式匹配

      模式匹配:只要等号俩边的模式相同,左边的变量就会被赋予对应的值。下面是解构列子

    let [foo,[[bar],baz]]=[1,[[2],3]];
    console.log(foo);//1
    console.log(bar);//2
    console.log(baz);//3
    
    let [ , , third]=['a','b','c'];
    console.log(third);//c
    
    let [x, ,y]=[5,6,7];
    console.log(x);//5
    console.log(y);//7
    
    let [head,...tail]=[1,2,3,4];
    console.log(head);//1
    console.log(tail);//[2, 3, 4]
    
    let [k,j,...z]=['a'];
    console.log(k);//a
    console.log(j);//undefined 
    console.log(z);//[]
    //注:如果解构不成功,变量的值就等于undefined。

    2、不完全解构

      即等号左边的模式,只匹配一部分的等号右边的数组,这种情况下,解构依然可以成功。

    let [x,y]=[1,2,3];
    x //1
    y //2
    
    let [a,[b],c]=[1,[2,3],4];
    a//1
    b//2
    c//4
    //注:var,let,const命令都适用解构赋值

    3、默认值

    [x,y='b']=['a'];
    console.log(x);//a
    console.log(y);//b
    //解构赋值允许指定默认值

    4、对象解构赋值

    var {foo,bar}={foo:'aaa',bar:'bbb'};
    console.log(foo);//aaa
    console.log(bar);//bbb

      注:对象解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。对象解构也可以指定默认值

    5、函数参数解构赋值

    function add([x,y]){
        return x+y;
    }
    add([1,2]);//3

      上面代码中,函数add的参数表面上是一个数组,但在传入参数的那一刻,数组参数就被解构成变量x和y。对于函数内部的代码来说,它们能感受的参数就是X和Y

    //函数参数的解构也可以使用默认值
    function move({x=0,y=0}={}){
        return [x,y];
    }
    move({x:3,y:8});//[3,8]
    move({x:3});//[3,0]
    move({});//[0,0]
    move();//[0,0]

      上面代码中,函数move的参数就是一个对象,通过对这个对象就行解构,得到变量x和y的值,如果解构失败,x和y等于默认值。

    三、字符串扩展

    1、for...of循环遍历

    var str ='html';
    for(let i of str){
        console.log(i);
    }
    //h
    //t
    //m
    //l
    //for of遍历将字符串每个字符都遍历出来了

    2、includes()、startsWith()、endsWith()

      includes():返回布尔值,表示是否找到了参数字符串。
      startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。
      endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。

    var str = 'Hello world!';
    str.stsrtsWith('world',6)//true
    str.endsWith('Hello',5)//true
    str.includes('Hello',6)//false

      上面代码表示,使用第二个参数n时,endsWith的行为与其他俩个方法有所不同,它针对前n个字符,而其他俩个方法针对从第n个位置直到字符串结束。

    3、repeat()

    //repeat方法返回一个新字符串,表示将原字符串重复n次。
    'x'.repeat(3);//'xxx'
    'hello'.repeat(2);//'hellohello'

    4、模板字符串

      模板字符串:是增强版的字符串,用反引号(`)标识,它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量,模板字符串中嵌入变量,需要将变量名写在${}之中。

    var name ='Bob',time='today';
    `hello ${name},how are you ${time}?`

     

    四、数值扩展

    1、二进制和八进制的表示法

      ES6提供了二进制和八进制数值的新写法,分别用前缀0b和0o表示。

    2、Number.isNaN()、Number.parseInt()、Number.parseFloat()

    //Number.isNaN()用来检查一个值是否为NaN
    Number.isNaN(NaN);//true
    Number.isNaN(15);//false
    Number.isNaN('15');//false
    console.log(Number.isNaN(true));//false
    //它们与传统的全局方法isNaN()的区别在于,传统方法先调用Number()将非数值的值转为数值,再进行判断,而该新方法只对数值有效,非数值一律返回false.

      

    //ES6将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变。
    //ES5写法
    parseInt('12.34');//12
    parseFloat('16.88#');//16.88
    
    //ES6写法
    Number.parseInt('12.34');//12
    Number.parseFloat('16.88#');//16.88
    //这样做的目的,是逐步减少全局性方法,使得语言逐步模块化

    3、Math对象的扩展

      Math.trunc():去除一个数的小数部分,返回整数部分

    Math.trunc(4.1);//4
    Math.trunc(4.9);//4
    Math.trunc(NaN);//NaN
    Math.trunc('foo');//NaN
    Math.trunc();//NaN
    //对于非数值,Math.trunc内部使用Number方法将其先转为数值,对于空值和无法截取整数的值,返回NaN.

      

      Math.sign():用来判断一个数到底是正说、负数、还是零。

    // 它会返回五中值:参数为正数,返回1;参数为负数,返回-1;参数为0,返回0;参数为-0,返回-0;其他值,返回NaN.
    Math.sign(-5);//-1
    Math.sign(5);//+1
    Math.sign(0);//+0
    Math.sign(-0);//-0
    Math.sign(NaN);//NaN
    Math.sign('foo');//NaN

      

      Math.cbrt():用于计算一个数的立方根

    Math.cbrt(8);//2
    Math.cbrt('27');//3
    Math.cbrt('hello');//NaN
    //对于非数值,Math.cbrt方法也是先使用Number方法将其转为数值。

      Math.hypot():返回所有参数的平方和的平方根

    Math.hypot(3,4);//5
    Math.hypot(3,'4');//5
    Math.hypot(3,4,'foo');//NaN
    //如果参数不是数值,Math.hypot方法将其转为数值,只要有一个参数无法转为数值,就会返回NaN.

    五、数组扩展

    1、Array.form()

    //Array.form():用于将俩类对象转为真正的数组:类似数组的对象和可遍历的对象
    var oList = document.getElementById('list');
    var aLi = Array.from(oList.children);
    //实际应用中,常见的类似数组的对象是DOM操作返回的NodeList集合,以及函数内部的arguments对象。Array.from都可以将它们转为真正的数组。

    2、数组实列find()和findIndex()

      数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。

    var arr =[1,5,6,9];
    var result = arr.find(function(v){
        if(v>5){
            return true;
        }else{
            return false;
        }
    });
    console.log(result);//6

      数组实例的findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1

    var arr =[1,5,6,9];
    var result = arr.find(function(v){
        if(v>5){
            return true;
        }else{
            return false;
        }
    });
    console.log(result);//2

    六、函数扩展

    1、函数参数默认值

    // ES6允许为函数的参数设置默认值,即直接写在参数定义的后面。
    function log(x,y='world'){
        console.log(x,y);
    }
    log('hello');//hello world
    log('hello',china);//hello china
    log('hello','')//hello

    2、参数变量是默认声明的,不能用let 或const再次声明。

    function foo(x=5){
        let x =1;//error
        const x=2;//error
    }
    // 上面代码中,参数变量x是默认声明的,在函数体中,不能用let或const再次声明,否则会报错。

    3、rest参数

      ES6引入rest参数(形式为“...变量名”),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

    function print(a,...b){
        console.log(a,b);
    }
    
    print(4,5,6,7,8,);//4,[5,6,7,8]

    4、扩展运算符

    //扩展运算符:扩展运算符(spread)是三个点(...)。它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列。
    console.log(...[1,2,3]);//1 2 3
    console.log(1,...[2,3,4],5);//1 2 3 4 5
    //扩展运算符应用一
    // 合并数组
    
    [1,2].concat(more);//ES5
    [1,2,...more];//ES6
    var arr1 = ['a','b'];
    var arr2 = ['c'];
    var arr3 = ['d','e'];
    
    //ES5的合并数组
    arr1.concat(arr2,arr3);//['a','b','c','d','e']
    //ES6的合并数组
    [...arr1,...arr2,...arr3];//['a','b','c','d','e'] 
    //扩展运算符应用二
    //与解构赋值结合
    const[first,...rest]=[1,2,3,4,5];
    first//1
    rest//[2,3,4,5]
    
    // 如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
    const[...first,last]=[1,2,3,4,5];//报错
    const[first,...middle,last]=[1,2,3,4,5];//报错
    //扩展运算符应用二
    //字符串:扩展运算符还可以将字符串转为真正的数组。
    [...'hello']//['h','e','l','l','o']

    5、严格模式

      从ES5开始,函数内部可以设定为严格模式。《ECMAScript 2016标准》做了一点修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。

    6、箭头函数

    //ES6允许使用“箭头”(=>)定义函数
    var f = v =>v;
    // 上面的箭头函数等同于
    var f = function(v){
        return v;
    }
    
    //如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。 var sum =(num1,num2)=>num1+num2; //等同于 var sum =function(num1,num2){ return num1+num2; }
    // this在箭头函数中的使用 1:在普通函数中,this指向的是window,在严格模式下,this指向的是undefined 2:在方法内,this指向的是方法的拥有者 3:在箭头函数内,this指向的是创建箭头函数时所在的环境中this指向的值。

     

    七、对象扩展

    1、属性的简洁表示法

    //ES6允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁
    function f(x,y){
        return{x,y};
    }
    //等同于
    function f(x,y){
        return{x:x,y:y};
    }
    f(1,2);//{x:1,y:2}

    2、Object.assign()

    //用于对象的合并,将源对象(source)的所有属性,复制到目标对象(target)
    var target ={a:1};
    var sourcel={b:2};
    var sourde2={c:3};
    Object.assign(target,sourcel,sourde2);
    target //{a:1,b:2,c:3}

      Object.assign方法的第一个参数是目标对象,后面的参数都是源对象。注意,如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。

    八、Set和Map

    1、Set

      ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。Set本身是一个构造函数,用来生成Set数据结构。

    var oSet = new Set([1,3,7]);
    oSet.add(4);
    oSet.add(7);
    console.log(oSet);
    
    // 数组去重
    var arr = [1,4,5,7,7,5,4,1];
    // (方法一)Array.from方法可以将Set结构转为数组
    console.log(Array.from(new Set(arr)));
    // (方法二)
    console.log([...new Set(arr)]);

     

       Set实例属性

    // constructor属性:构造函数,默认就是Set函数。
    // size属性:返回Set实例的成员总数。
    var oSet = new Set([2,5,8]);
    console.log(oSet.constructor);
    console.log(oSet.size);

      Set实列方法

      Set实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)

    //(操作方法)
    //add(value):添加某个值,返回Set结构本身。
    //delete(value):删除某个值,返回一个布尔值,表示删除是否成功
    //has(value):返回一个布尔值,表示该值是否为Set的成员。
    //clear():清除所有成员,没有返回值。
    //(遍历方法)
    //keys(),values(),entries():keys方法、values方法、entries方法返回的都是遍历器对象。
    //由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。
    let set = new Set(['red','green','blue']); for(let item of set.keys()){ console.log(item); } // red // green // blue for(let item of set.values()){ console.log(item); } // red // green // blue for(let item of set.entries()){ console.log(item); } //['red','red'] //['green','green'] //['blue','blue'] //上面代码中,entries方法返回的遍历器,同时包括键名和键值,所以每次输出一个数组,它的两个成员完全相等。

    2、Map

      JavaScript的对象(Object),本质上是键值对的集合,但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。为了解决这个问题,ES6提供了Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应。如果你需要“键值对”的数据结构,Map比Object更合适

    var o = new Map();
    o.set('name','张三');
    o.set('function(){}','567');

      

      Map实列的属性和方法 

    
    

    //size属性:返回Map结构的成员总数。
    //set(key, value):set方法设置key所对应的键值,然后返回整个Map结构。如果key已经有值,则键值会被更新,否则就新生成该键。set方法返回的是Map本身,因此可以采用链式写法。
    //get(key):get方法读取key对应的键值,如果找不到key,返回undefined
    //has(key):返回一个布尔值,表示某个键是否在Map数据结构中。
    //delete(key):删除某个键,返回true。如果删除失败,返回false。
    //clear():清除所有成员,没有返回值。

     

     

    九、Gennerator生成器

      Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同。从语法上,可以把它理解成,Generator函数是一个状态机,封装了多个内部状态。形式上,Generator函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield语句,定义不同的内部状态(yield语句在英语里的意思就是“产出”)。执行Generator函数会返回一个遍历器对象,也就是说,Generator函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历Generator函数内部的每一个状态。

    //generator生成器函数
    function* sum(a,b){
        var c = a + b;
        yield c;
        var d = c + a;
        yield d;
        var e = d + b;
        yield e;
        var f = c + d +e;
        return f;
    }
    var o = sum(4,7);
    console.log(o.next());//{value: 11, done: false}
    console.log(o.next());//{value: 15, done: false}
    console.log(o.next());//{value: 22, done: false}
    console.log(o.next());//{value: 48, done: true}
    console.log(o.next());//{value: undefined, done: true}

      上面代码中的next方法,是使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield语句(或return语句)为止。换言之,Generator函数是分段执行的,yield语句是暂停执行的标记,而next方法可以恢复执行。

      yield语句:由于Generator函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield语句就是暂停标志。
    遍历器对象的next方法的运行逻辑如下:
      1:遇到yield语句,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。
      2:下一次调用next方法时,再继续往下执行,直到遇到下一个yield语句。
      3:如果没有再遇到新的yield语句,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。
      4:如果该函数没有return语句,则返回的对象的value属性值为undefined。

       next()方法:会执行generator的代码,然后,每次遇到yield x;就返回一个对象{value: x, done: true/false},然后“暂停”。返回的value就是yield的返回值,done表示这个generator是否已经执行结束了。如果done为true,则value就是return的返回值。当执行到done为true时,这个generator对象就已经全部执行完毕,不要再继续调用next()了。

     

    十、class的写法

      俗话说物以类聚人以群分,具有一定的共同属性或特点的,就是calss来定义的类。

    // 定义一个类
    class Dog{
        constructor(name,age){
            this.name = name;
            this.age  = age;
        }
        bark(){
            console.log('旺旺!')
        }
    }
    var oDaHuang = new Dog('大黄',3);
    var oWangCai = new Dog('旺财',4);
    
    console.log(oDaHuang);
    console.log(oWangCai);
    
    oDaHuang.bark();
    oWangCai.bark();
    // 上面代码表明,类的数据类型就是函数,类本身就指向构造函数。
    // 使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致
    // constructor方法:是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。

      

      类的继承

    //Class之间可以通过extends关键字实现继承
    // 下面代码定义了一个类Dog,该类通过extends关键字,继承了Animal类的所有属性和方法。
    
    class Animal {
        constructor() {
            this.legs = 4;
        }
        eat() {
            console.log('吃东西!');
        }
    }
    
    class Dog extends Animal{
        constructor(name, age) {
            super();
            // uper作为对象时,指向父类的原型对象。
            this.name = name;
            this.age  = age;
        }
        bark() {
            super.eat();
            // super作为函数调用时,代表父类的构造函数。
            console.log('啊呜!');
        }
        sleep() {
            console.log('睡觉!');
        }
    }
    
    
    var oDaHuang = new Dog('大黄', 3);
    var oWangCai = new Dog('旺财', 4);
    
    oDaHuang.bark();
    
    console.log(oDaHuang);
    // {legs: 4, name: "大黄", age: 3}
    console.log(oWangCai);
    //{legs: 4, name: "旺财", age: 4}

      

      super关键字:既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。

      第一种情况,super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。

      注:作为函数时,super()只能用在子类的构造函数之中,用在其他地方就会报错

      第二种情况,super作为对象时,指向父类的原型对象。

      知识渊博,认知有限,欢迎各位好友留言指点,互相学习,共同进步

      

  • 相关阅读:
    GridControl 选择列、复选框全选(上)
    iOS开发隐藏键盘方法总结
    Leetcode:remove_duplicates_from_sorted_list
    新版的Spring4X怎样下载
    leetcode 242: Valid Anagram
    最简单的基于FFMPEG的Helloworld程序
    电子商务系统的设计与实现(十):DWZ框架与第三方分页组件整合
    电子商务系统的设计与实现(十):DWZ框架与第三方分页组件整合
    电子商务系统的设计与实现(九):后端管理系统功能细化
    电子商务系统的设计与实现(九):后端管理系统功能细化
  • 原文地址:https://www.cnblogs.com/MaShuai666/p/8407179.html
Copyright © 2011-2022 走看看