es6_es6- - CSDN
es6 订阅
ECMAScript 6(简称ES6)是于2015年6月正式发布的JavaScript语言的标准,正式名为ECMAScript 2015(ES2015)。它的目标是使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言 [1]  。另外,一些情况下ES6也泛指ES2015及之后的新增特性,虽然之后的版本应当称为ES7、ES8等。 展开全文
ECMAScript 6(简称ES6)是于2015年6月正式发布的JavaScript语言的标准,正式名为ECMAScript 2015(ES2015)。它的目标是使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言 [1]  。另外,一些情况下ES6也泛指ES2015及之后的新增特性,虽然之后的版本应当称为ES7、ES8等。
信息
简    称
ES6
类    型
前端语言
通过日期
2015年6月
性    质
JavaScript语言的标准
正式名字
ECMAScript 2015(ES2015)
外文名
ECMAScript 6
ECMAScript 6发展历史
2000年,ECMAScript 4.0开始酝酿。这个版本最后没有通过,但是它的大部分内容被ECMAScript6继承了。因此,ECMAScript6制定的起点其实是2000年。2007年10月,ECMAScript 4.0草案发布,本来预计2008年8月发布正式版本。但是,各方对于是否通过这个标准,发生了严重分歧。以Yahoo、Microsoft、Google为首的大公司,反对JavaScript的大幅升级,主张小幅改动;以JavaScript创造者Brendan Eich为首的Mozilla公司,则坚持当前的草案。2008年7月,由于对于下一个版本应该包括哪些功能,各方分歧太大,争论过于激烈,ECMA开会决定,中止ECMAScript 4.0的开发,将其中涉及现有功能改善的一小部分,发布为ECMAScript 3.1,而将其他激进的设想扩大范围,放入以后的版本,由于会议的气氛,该版本的项目代号起名为Harmony(和谐)。会后不久,ECMAScript 3.1就改名为ECMAScript 5。2009年12月,ECMAScript 5.0正式发布。Harmony项目则一分为二,一些较为可行的设想定名为 JavaScript.next继续开发,后来演变成ECMAScript 6;一些不是很成熟的设想,则被视为JavaScript.next.next,在更远的将来再考虑推出。2011年,ECMAScript 5.1发布后开始6.0版的制定。2013年3月,ECMAScript 6草案冻结,不再添加新功能。新的功能设想将被放到ECMAScript 7。2013年12月,ECMAScript 6草案发布。然后是12个月的讨论期,听取各方反馈。由于这个版本引入的语法功能太多,而且制定过程当中,还有很多组织和个人不断提交新功能。标准委员会最终决定,标准在每年的6月份正式发布一次,作为当年的正式版本。接下来的时间,就在这个版本的基础上做改动,直到下一年的6月份,草案就自然变成了新一年的版本。2015年6月,ECMAScript 6(ES6)正式通过,成为国际标准,正式名称是“ECMAScript 2015”(简称ES2015)。2016年6月,小幅修订的“ECMAScript 2016”(简称ES2016或ES7)标准发布,相当于ES6.1版,因为两者的差异非常小(只新增了数组实例的includes方法和指数运算符),基本上是同一个标准 [1]  。
收起全文
  • 你必须知道的ES6语法

    2020-03-02 10:26:29
    初步掌握 ES6 中常用和核心的语法,并能运用到实际开发中。 通过每节一个语法的方式,详细解析ES6中重要语法的使用和应用场景,如解构赋值,箭头函数等热门语法,课程内容短小,侧重实战和学员动手能力,通过本...
  • ES6简介 1.什么是ES6? ES的全称是ECMAScript,它是由ECMA国际标准化组织制定的一项脚本语言的标准化规范。 年份 版本 2015年6月 ES2015 2016年6月 ES2016 2017年6月 ES2017 2018年6月 ES2018 … ...

    ES6简介

    1.什么是ES6?

    • ES的全称是ECMAScript,它是由ECMA国际标准化组织制定的一项脚本语言的标准化规范。
    年份 版本
    2015年6月 ES2015
    2016年6月 ES2016
    2017年6月 ES2017
    2018年6月 ES2018
    • ES6实际上是一个泛指,泛指ES2015及后续的版本。(每年的6月更新一次,区别以年份命名,如:ES2015)。

    2.为什么要使用ES6?

    • 因为JavaScript本身存在的缺陷,如:

      (1)变量提升特性增加了程序运行时的不可预测性。

      (2)语法过于松散,实现相同的功能,不同的人可能会写出不同的代码。

    ES6的新增语法

    1.let

    • let是ES6中新增的用于声明变量的关键字。

    (1)let声明的变量只在所处的块级有效。

    if(true){
      let a = 10;
    }
    console.log(a);		//报错,a is not defined
    

    注意:在一个大括号中,使用let关键字声明的变量才具有块级作用域,使用var声明的变量不具备块级作用域特性。

    • let可以防止循环变量变成全局变量。
    for(var i = 0; i<2; i++){
      
    }
    console.log(i);		//输出结果为:2
    
    for(let i = 0; i<2; i++){		//虽然这个i没有写在大括号中,但是它却和这个for循环绑定在一起,块级作用域外是访问不到这个变量的
      
    }
    console.log(i);		//输出结果为:报错,i is not defined
    
    

    (2)不存在变量提升。

    console.log(a);		//报错,a is not defined
    let a = 20;
    

    (3)暂时性死区(在块级作用域内使用let声明的变量会被整体绑定在这个块级作用域内,不受外部代码影响)。

    //例子1:
    var num = 10;
    if(true){
      console.log(num);		
      let num =20;				//因为在块级作用域内使用了let关键字声明变量,所以这个变量和整个块级作用域进行了绑定,所以执行console.log(num)时,会报num没有定义的错误
    }
    //例子2:
    var tmp = 123;
    if(true){
      tmp = 'abc';		//因为该块级作用域使用了let tmp;所以tmp和该块级作用域整体进行了绑定,没先声明就赋值就会出现错误(因为没有变量提升的功能)。如果只有tmp = 'abc';那么就会隐式声明,该tmp变量会成为全局变量。如果只有var tmo =123;和tmp = 'abc';那么就会修改tmp的值。
      let tmp;
    }
    //拓展:
    function test(){
      console.log(a);		//会报错,因为a = 3存在隐式声明,而隐式声明没有变量提升的功能,所以会报错
      
      a = 3;
      
    }
    
    
    
    
    

    (4)不可重复声明。

    • 简单面试题:
    var arr = [];
    for(var i = 0; i < 2; i++){
      arr[i] = function(){
        console.log(i);
      }
    }
    arr[0]();		//输出结果为:2
    arr[1]();		//输出结果为:2
    

    在这里插入图片描述

    • 解题关键:变量i是全局的,函数执行时输出的都是全局作用域下的值。
    var arr = [];
    for(let i = 0; i < 2; i++){
      arr[i] = function(){
        console.log(i);
      }
    }
    arr[0]();		//输出结果为:0
    arr[1]();		//输出结果为:1
    

    在这里插入图片描述

    • 解题关键:每次循环都会产生一个块级作用域,每个块级作用域中的变量都是不同的,函数执行时输出的是自己上一级(循环产生的块级作用域)作用域中的i值。

    • 注意:

    let num = 10;
    if (true) {
      console.log(num);
    }		//如果在大括号内没有用let声明变量,但大括号外面用let声明了该变量,那么大括号是可以使用大括号外面声明的该变量的(跟var一样)
    
    

    2.const

    • 作用:声明常量,常量就是值(内存地址)不能变化的量。

    (1)具有块级作用域。

    if(true){
      const a = 10;
    }
    console.log(a);		//报错,a is not defined
    

    (2)声明常量时必须赋值(否则就会报错)。

    const PI;		//报错,Missing initializeri in const declaration
    

    (3)常量赋值后,值不能修改。

    const PI = 3.14;
    PI = 100;		//报错,Assignment to constant variable
    
    const ary = [100,200];
    arr[0] = 'a';
    arr[1] = 'b';
    console.log(arr);		//输出结果为:['a','b']
    arr = ['a','b'];		//报错,Assignment to constant variable
    

    (4)不可重复声明。

    3.let、const、var的区别

    • 使用var 声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象。
    • 使用let声明的变量,其作用域为该语句所在的代码块内,不存在变量提升。
    • 使用const声明的是常量,在后面出现的代码中不能再修改该常量的值。
    var let Const
    函数级作用域 块级作用域 块级作用域
    变量提升 不存在变量提升 不存在变量提升
    值可更改 值可更改 值不可更改
    • 如果存储的值不需要变化,推荐使用const。比如:函数的定义、对象、数学公式中的一些恒定不变的值。

    4.解构赋值

    • ES6中允许从数组中提取值,按照对应位置,对变量赋值。对象也可以实现解构。
    • 解构赋值的拷贝是浅拷贝,即如果一个键的值是复合类型的值(数组、对象、函数),那么解构赋值拷贝的是这个值的引用(即地址),而不是这个值的副本。
    (1)数组解构
    • 数组解构允许按照一一对应的关系从数组中提取值,然后将值赋值给变量。
    let [a,b,c] = [1,2,3];
    console.log(a);		//输出结果为:a
    console.log(b);		//输出结果为:b
    console.log(c);		//输出结果为:c
    
    • 如果解构不成功,变量的值为undefined。
    let [a] = [];				//a的值为undefined
    let [a,b] = [1];		//a的值为1,b的值为undefined
    
    (2)对象解构
    • 对象解构允许使用变量的名字匹配对象的属性,匹配成功则将对象属性的值赋值给变量。
    let person = {name:'andy', age:22};
    let {name,age} = person;
    console.log(name);		//输出结果为:'andy'
    console.log(age);			//输出结果为:22
    
    let person = {name:'andy', age:22};
    let { name:myName, age:myAge} = person;		//myName和myAge属于别名
    console.log(myName);		//输出结果为:'andy'
    console.log(myAge);			//输出结果为:22
    

    5.箭头函数

    • 箭头函数是ES6新增的定义函数的方式。
    () => {}
    const fn = () => {}		//通常将箭头函数赋值个一个变量,变量名字就是箭头函数的名字
    
    • 函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号。
    //有return返回值的情况:
    //传统的函数:
    function sum(num1,num2){
      reutrn num1 + num2;
    }
    //箭头函数:
    const sum = (num1,num2) => num1 + num2;
    
    //没有return返回值的情况:
    //传统的函数:
    function fn(){
      console.log('Hello!');
    }
    //箭头函数:
    const fn = () => console.log('Hello!');
    
    • 如果形参只有一个,则可以省略小括号。
    //传统的函数:
    function fn(v){
      return v;
    }
    //箭头函数:
    const fn = v => v;
    
    • 箭头函数不绑定this关键字,箭头函数中的this,指向的是函数定义位置的上下文this。
    const obj = { name: 'andy'};
    function fn(){
      console.log(this);		//输出结果为:{ name: 'andy'}
      return () => {
        console.log(this);	//输出结果为:{ name: 'andy'}
      }
    }
    const resFn = fn.call(obj);
    resFn();		//尽管调用的是window下的resFn函数,但因为它是箭头函数,它没有自己的this关键字,如果使用this,则指向箭头函数定义位置的this。由于箭头函数定义位置的this已经被修改为obj,所以此时箭头函数的this就指向obj对象。
    
    
    
    • 箭头函数面试题:
    var obj = {
      age: 22,
      say: () => {
        alert(this.age);	//该this定义在obj对象内,但因为obj对象不能产生作用域,所以箭头函数中的this指向的是window,但由于window下面没有定义age变量,所以输出结果为undefined。	
      }
    }
    obj.say();
    
    

    6.剩余参数

    • 剩余参数允许将一个不定数量的参数表示为一个数组。
    function sum(first,...args){
      console.log(first);	//10
      console.log(args);	//[20,30]
    }
    sum(10,20,30);
    //注意:箭头函数使用不了argument
    
    • 剩余参数和解构配合使用。
    let students = ['andy','Mary','lebron'];
    let [s1,...s2] = students;
    console.log(s1);		//输出结果为:'andy'
    console.log(s2);		//输出结果为:['Mary','lebron']
    

    ES6的内置对象扩展

    1.Array的扩展方法

    (1)扩展运算符(展开语法)
    • 扩展运算符可以将数组或者对象转为用逗号分隔的参数序列。(扩展运算符后面可以跟表达式)

    注意:用扩展运算符对数组或者对象进行拷贝时,只能扩展和深拷贝第一层的值,对于第二层极其以后的值,扩展运算符将不能对其进行打散扩展,也不能对其进行深拷贝,即拷贝后和拷贝前第二层中的对象或者数组仍然引用的是同一个地址,其中一方改变,另一方也跟着改变。

    let ary = [1,2,3];
    ...ary;		//1,2,3
    console.log(...ary);	//1 2 3
    console.log(1,2,3);		//1 2 3
    
    let n = {a:2,b:4};
    let m = {...n};
    m		//{a:2,b:4}
    //深拷贝
    n.a = 6;
    console.log(n);		//{a:6,b:4}
    console.log(m);		//{a:2,b:4}
    
    • 扩展运算符可以应用于合并数组。(注意:如果用户自定义的属性放在扩展运算符后面,则扩展运算符内部的同名属性会被覆盖掉)
    //方法一:
    let ary1 = [1,2,3];
    let ary2 = [4,5,6];
    let ary3 = [...ary1, ...ary2];	//[1,2,3,4,5,6]
    //方法二:
    ary1.push(...ary2);		//[1,2,3,4,5,6]
    
    //用户自定义的属性放在扩展运算符后面
    const a = {
      x: 5,
      y: 6,
      n: 7,
      m: 8
    }
    let ary = {..a,x:1,y:2};
    console.log(ary);		//输出结果为:{x:1,y:2,n:7,m:8}
    
    • 扩展运算符可以将类数组(伪数组)或可遍历对象转换为真正的数组。
    let oDivs = document.getElementsByTagName('div');
    oDivs = [...oDivs];
    
    (2)构造函数方法:Array.from()
    • 将类数组(伪数组)或可遍历对象转换为真正的数组。
    let arrayLike = {
      '0': 'a',
      '1': 'b',
      '2': 'c',
      'length': 3
    };
    let arr = Array.from(arrayLike);		//['a','b','c']
    
    • 该方法还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。
    let arrayLike = {
      '0': 1,
      '2': 2,
      'length': 2
    };
    let newAry = Array.from(arrayLike,item => item * 2);	//[2,4]
    
    (3)实例方法:find()
    • 用于找出第一个符合条件的数组成员。如果找到就返回该元素值;如果没有找到就返回undefined。
    let ary = [{
      id: 1,
      name: 'andy'
    },{
      id: 2,
      name: 'Mary'
    }];
    let target = ary.find(item => item.id == 2);	//item.id==2就是循环体,就是查找条件,return和大括号省略了
    console.log(target);		//输出结果为:{id: 2, name: 'Mary'}
    
    
    (4)实例方法:findIndex()
    • 用于找出第一个符合条件的数组成员的位置,如果没有找到就返回-1。
    let ary = [1,5,10,15];
    let index = ary.findIndex(value => value > 9);
    console.log(index);		//输出结果为:2
    
    (5)实例方法:includes()
    • 表示某个数组是否包含给定的值,返回布尔值。
    let a = [1,2,3].includes(2);		
    console.log(a);		//true
    let b = [1,2,3].includes(4);
    console.log(b);		//false
    

    2.String的扩展方法

    (1)模版字符串
    • 模版字符串是ES6新增的创建字符串的方式,使用反引号定义。
    let name = `andy`;
    
    • 模版字符串中可以解析变量。
    let name = 'andy';
    let sayHello = `hello,my name is ${name}`;	
    console.log(sayHello);		//输出结果为:hello,my name is andy
    
    • 模版字符串中可以换行。
    let result = {
      name: 'andy',
      age: 22
    };
    let html = `
    		<div>
    				<span>${result.name}</span>
    				<span>${result.age}</span>
    		</div>
    `;
    console.log(html);		//输出结果也是换行的
    
    • 在模版字符串中可以调用函数。
    const fn = () => {
      return '我是andy!'
    }
    let html = `你好,${fn()}`;
    console.log(html);		//输出结果为:你好,我是andy!
    
    (2)实例方法:startsWith()和endsWith()
    • startsWith():表示参数字符串是否在原字符串的头部,返回布尔值。
    • endWith():表示参数字符串是否在原字符串的尾部,返回布尔值。
    let str = 'Hello world!';
    let a = str.startsWith('Hello');
    console.log(a);		//输出结果为:true
    let b = str.endsWith('!');
    console.log(b);		//输出结果为:true
    
    (3)实例方法:repeat()
    • repeat()方法表示将字符串重复n次,返回一个新字符串。
    let a = 'x'.repeat(4);
    console.log(a);		//输出结果为:'xxxx'
    let b = 'hello'.repeat(2);
    console.log(b);		//输出结果为:'hellohello'
    

    3.Set数据结构

    • ES6提供了新的数据结构Set。它类似于数组,也是用来存储数据的,但它的成员的值都是唯一的,没有重复的值。
    • Set本身是一个构造函数,实例化该构造函数就是生成Set数据结构。
    const s = new Set();
    
    • Set函数可以接受一个数组作为参数,用来初始化。
    const set = new Set([1,2,3,4,5]);
    
    • Set数据结构有size属性,可以返回Set数据结构中存储了多少数据。
    const s = new Set([1,2,3]);
    console.log(s.size);		//输出结果为:3
    
    • 通过Set数据结构,可以实现数组去重。
    const a = ['a','a','b','b'];
    const s = new Set(a);
    var ary = [...s];
    console.log(ary);		//输出结果为:['a','b']
    
    • 实例方法:

      (1)add(value):添加某个值,返回Set结构本身。

      (2)delete(value):删除某个值,返回一个布尔值表示删除是否成功。

      (3)has(value):返回一个布尔值,表示该值是否为Set的成员。

      (4)clear():清除所有成员,没有返回值。

      const s = new Set();
      s.add(1).add(2).add(3);
      console.log(s);		//输出结果为:3
      const a = s.delete(3);
      console.log(a);		//输出结果为:true
      cosnt b = s.has(2);
      console.log(b);		//输出结果为:true
      s.clear();
      console.log(s.size);	//输出结果为:0
      
    • 遍历:

      Set结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。(通过遍历,才可以取到Set数据结构中的值)

      const s = new Set(['a','b','c']);
      s.forEach(value => console.log(value));		//依次输出a、b和c
      

    注意:forEach()方法无法在所有元素都传递给调用的函数之前终止遍历,所以没有返回值,不能使用return。


    微信公众号也会定期更新,觉得文章写得还可以的,可以加个关注!点个赞!谢谢!

    在这里插入图片描述

    展开全文
  • ES6最常用语法

    ES6常用特性

    let,const

    ES5只有全局作用域和函数作用域,没有块级作用域,let则为JavaScript新增了块级作用域,用它所声明的变量,只在let命令所在的代码块内有效。

    {
        {
            let a = 1;
            let b = 1;
            console.log(a);// 1
            console.log(b);// 1
        }
        console.log(a);// a is not defined
        console.log(b);// 1
        for (let i = 0; i < 10; i++) {
            //...
        }
        console.log(i); // i is not defined
        for (var j = 0; j < 10; j++) {
            //...
        }
        console.log(j);//10
    }

    const也用来声明常量, 一旦声明,常量的值就不能改变。

    const PI = 3.1415926
    PI = 3.14;// Assignment to constant variable

    默认参数

    ES6之前处理默认参数,基本使用以下方法

    function toDecimal(number, precision) {
        var precision = precision||2;
        var temp = 1;
        for (let i = 0; i < precision; i++) {
            temp = temp * 10;
        }
        return Math.round(number * temp)/temp;
    }

    ES6则可以采用如下写法

    function toDecimal(number, precision = 2) {
        var temp = 1;
        for (let i = 0; i < precision; i++) {
            temp = temp * 10;
        }
        return Math.round(number * temp)/temp;
    }

    模板对象

    ES6之前拼接字符串对象,一般使用如下方式

    var firstName = 'Fei';
    var lastName = 'Zhang';
    console.log('your name is ' + firstName + ' ' + lastName + '.');

    ES6中可以使用${}的方式来做

    var firstName = 'Fei';
    var lastName = 'Zhang';
    console.log('your name is ${firstName} %{lastName}.');

    多行字符串

    ES6之前字符串换行,一般使用\n

    var strTemp = 'abcdefg\n'+
        'hijklmn\n';
    console.log(strTemp);

    ES6只需要反引号即可

    var strTemp = ·abcdefg
        hijklmn·;
    console.log(strTemp);

    箭头函数=>

    =>可以是function写法简洁清晰很多

    function(i){ return i + 1; } // ES5
    (i) => i + 1; // ES6
    
    function(x, y) {
        x++;
        y--;
        return x + y;
    }
    (x, y) => {x++; y--; return x+y}
    

    当使用匿名函数时,不需要额外定义变量指向this

    // ES5
    var obj = this;
    foo(function(){
        obj.foo1();
    });
    // ES6
    foo(()=>{this.foo1();});

    解构(Destructuring)

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

    let cat = 'cat'
    let dog = 'dot'
    let zoo = {cat: cat, dog: dog} // es5
    let zoo1 = {cat, dog} // es6
    // 也可以反过来写
    let {cat1, dog1} = zoo;

    rest参数(…变量名)

    rest参数搭配的变量是一个数组,可以使用数组的一切操作

    function sum(...values){
        let temp = 0;
        for (var value of values){
            temp = temp+ value;
        }
        return temp;
    }
    展开全文
  • 全面学习vue.js配置,es6命令,解构赋值,symbol类型,set,weakSet,Map,WeakMap,Iterator遍历器,Generator函数结构,Promise对象,async函数,箭头函数,class类,proxy代理,Decorator修饰器,model模块,二进制...
  • 【新】ES6精讲 ECMAscript6精通系列——基础篇 本课程作为福利,感谢很久以来同学们的支持!!
  • es6

    2020-05-20 16:56:53
    ES6中新增let关键字,用来声明局部变量。let关键字声明的变量只在局部代码块有用,超出作用域内将不再生效, 不同于var关键字,全局有效 例如如下实例 function varAndConst() { let i = 10; var j = 12; } console...
    1.let const
    
        let
    ES6中新增let关键字,用来声明局部变量。let关键字声明的变量只在局部代码块有用,超出作用域内将不再生效, 不同于var关键字,全局有效
    例如如下实例
    function varAndConst() {
        let i = 10;
        var j = 12;
    }
    console.log(i);//提示未定义
    console.log(j);//输出12
    
    let关键字具有块级作用域,超出作用域将不在生效。因此适用于循环,判断等只在块级作用域生效的变量
    例如如下实例
    function sum(n) {
        let num = 0;
        for (let i = 1; i < n; n++) {
            num += i;
        }
        return num;
    }
    
    const
        constES6中用来声明一个只读的常量的关键字。
        常量一旦声明将不可更改。
        const关键字的作用域与let相同,具有块级作用域
    const str = "This is const"
    console.log(str)//输出 This is const
    当尝试将str改变时将会执行错误
    str = "change This is const"//TypeError: Assignment to constant variable.
    const常量实际上指向的是常量的地址,并非引用本身。执行地址的变量值不会修改
    
    2.解构赋值
    
    数组结构赋值
    let [i, j, k] = ["i", "j", "k"];
    相当于
    let i = 'i', j = 'j', k = 'k';
    
    对象解构赋值
    let { fruit, animal } = { fruit: 'apple', animal: 'dog' };
    console.log(fruit);//输出apple
    console.log(animal);//输出dog
    
    字符串解构赋值
    const [h, e, l, l, o] = 'hello';
    h // "h"
    e // "e"
    l // "l"
    l // "l"
    o // "o"
    
    
    数值和布尔值的解构和赋值
    解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。
    let { toString: str } = 11;
    str === Number.prototype.toString // true
    let { toString: bool } = true;
    bool === Boolean.prototype.toString // true
    
    函数参数的解构赋值
    function mod([i, j]) {
        return x / y;
    }
    mod([1, 2]);
    
    
    3.字符串新增方法
    String.fromCodePeople()
    函数从 Unicode 码点返回对应字符,该方法不能识别码点大于0xFFFF的字符。
    
    String.raw()
    函数返回一个斜杠都被转义的字符串,往往用于模板字符串的处理方法。
    String.raw`Hello\n${6 * 10}!`
    // 实际返回 "Hi\\n5!",显示的是转义后的结果 "Hello\n60!"
    
    includes()
    函数返回布尔值,表示是否找到了参数字符串。
    hello.includes("hell")//返回true
    
    startsWith()
    函数返回布尔值,表示参数字符串是否在原字符串的头部。
    "hello".startsWith("h");//返回true"
    "hello".startsWith("o");//返回false
    
    endsWith()
    函数返回布尔值,表示参数字符串是否在原字符串的尾部。
    "hello".endsWith('o') // true
    "hello".includes('h') // true
    
    repeat方法返回一个新字符串,表示将原字符串重复n次。
    'hello'.repeat(2) // "hellohello"
    
    
    trimStart()
    删除字符串头部的空格,
    
    trimEnd()
    删除字符串结尾的空格。
    
    const s = '  hello  ';
    s.trim() // "hello"
    s.trimStart() // "hello  "
    s.trimEnd() // "  hello"
    
    matchAll()
    matchAll()方法返回一个正则表达式在当前字符串的所有匹配
    
    4.数值函数
    
    Number.isFinite(), Number.isNaN()
    Number.isFinite()用来检查一个数值是否为有限的(finite)
    Number.isFinite(15); // true
    Number.isFinite('Hello'); // false
    Number.isNaN()用来检查一个值是否为NaN
    Number.isNaN(NaN) // true
    Number.isNaN(1) // false
    
    Number.parseInt(), Number.parseFloat()
    ES6 将全局方法parseInt()和parseFloat() ,移植到Number对象上面,行为完全保持不变。
    // ES5的写法
    parseInt('3.14') // 3
    // ES6的写法
    Number.parseInt('3') // 3
    
    Number.isInteger()
    Number.isInteger()用来判断一个数值是否为整数
    Number.isInteger(3) // true
    Number.isInteger(3.14) // false
    
    Math.trunc()
    Math.trunc()
    取整函数,函数用于去除一个数的小数部分,返回整数部分。
    Math.trunc(3.14) // 3
    
    Math.sign()
    函数用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。
    它会返回五种值。
    参数为正数,返回 + 1;
    参数为负数,返回 - 1;
    参数为 0,返回0;
    参数为 - 0,返回 - 0;
    其他值,返回NaN。
    Math.sign(-2) // -1
    Math.sign(2) // +1
    Math.sign(0) // +0
    Math.sign(-0) // -0
    Math.sign(NaN) // NaN
    
    Math.cbrt()
    函数用于计算一个数的立方根
    Math.cbrt(-8) // -2
    Math.cbrt(0)  // 0
    Math.cbrt(1)  // 1
    Math.cbrt(8)  // 2
    
    Math.clz32()
    函数将参数转为 32 位无符号整数的形式,然后返回这个 32 位值里面有多少个前导 0。
    Math.clz32(0) // 32
    Math.clz32(1) // 31
    
    Math.imul()
    函数返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。
    Math.imul(2, 4)   // 8
    
    Math.fround()
    函数返回一个数的32位单精度浮点数形式。
    Math.fround(0)   // 0
    Math.fround(1)   // 1
    
    Math.hypot()
    函数返回所有参数的平方和的平方根。
    Math.hypot(3, 4);        // 5
    
    Math.log1p()
    函数返回1 + x的自然对数,即Math.log(1 + x) 。如果x小于 - 1,返回NaN。
    Math.log1p(1)  // 0.6931471805599453
    Math.log1p(0)  // 0
    
    Math.log10()
    函数返回以 10 为底的x的对数。如果x小于 0,则返回 NaN。
    Math.log10(0)      // -Infinity
    Math.log10(100000) // 5
    
    
    Math.log2()
    函数返回以 2 为底的x的对数。如果x小于 0,则返回 NaN。
    Math.log2(8)    // 3
    
    
    双曲函数方法
    ES6 新增了 6 个双曲函数方法。
    Math.sinh(x) 返回x的双曲正弦(hyperbolic sine)
    Math.cosh(x) 返回x的双曲余弦(hyperbolic cosine)
    Math.tanh(x) 返回x的双曲正切(hyperbolic tangent)
    Math.asinh(x) 返回x的反双曲正弦(inverse hyperbolic sine)
    Math.acosh(x) 返回x的反双曲余弦(inverse hyperbolic cosine)
    Math.atanh(x) 返回x的反双曲正切(inverse hyperbolic tangent)
    
    指数运算符
    ES2016 新增了一个指数运算符(**)。
    2 ** 2 // 4
    2 ** 3 // 8
    
    
    5.函数新增属性
    
    函数参数的默认值
    ES6 之前,不能直接为函数的参数指定默认值,ES6 允许为函数的参数设置默认值,如下实例
    function log(x = 'Hello ', y = 'World') {
        console.log(age, sex);
    }
    console.log('Hello world') // Hello World
    
    rest 参数
    ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数
    function add(...values) {
        let sum = 0;
    
        for (var val of values) {
            sum += val;
        }
    
        return sum;
    }
    add(2, 5, 3) // 10
    
    
    name 属性
    函数的name属性,返回该函数的函数名
    function hello() { }
    hello.name // "hello"
    
    箭头函数 最方便的数据处理函数
    var sum = (i, j) => i + j;
    // 等同于
    var sum = function (i, j) {
        return i + j;
    };
    
    
    6.数组新增属性
    
    扩展运算符
    扩展运算符(spread)是三个点(...),它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
    console.log(...["hello", " world", '!'])//hello world!
    
    复制数组
    es6以前
    const a1 = [1, 2];
    const a2 = a1.concat();
    a2[0] = 2;
    a1 // [1, 2]
    
    es6用法
    const a1 = [1, 2];
    // 写法一
    const a2 = [...a1];
    
    
    
    合并数组
    const arr1 = ['a', 'b'];
    const arr2 = ['c'];
    // ES5 的合并数组
    arr1.concat(arr2);
    // [ 'a', 'b', 'c' ]
    // ES6 的合并数组
    [...arr1, ...arr2]
    // [ 'a', 'b', 'c']
    
    字符串
    [...'hello']
    // [ "h", "e", "l", "l", "o" ]
    
    Array.from()
    Array.from方法用于将两类对象转为真正的数组
    let arrayByObject = {
        '0': 'a',
        '1': 'b',
        '2': 'c',
    };
    // ES5的写法
    var arr1 = [].slice.call(arrayByObject); // ['a', 'b', 'c']
    // ES6的写法
    let arr2 = Array.from(arrayByObject); // ['a', 'b', 'c']
    
    Array.of()
    Array.of方法用于将一组值,转换为数组。
    Array.of(3, 11, 8) // [3,11,8]
    
    
    find()
    数组实例的find方法,用于找出第一个符合条件的数组成员。
    [1, 4, -5, 10].find((n) => n > 5)
    // 10
    
    findIndex()
    数组实例的findIndex返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回 - 1[1, 5, 10, 15].findIndex((n) => n > 5) // 3
    
    数组实例的 fill()
    fill方法使用给定值,填充一个数组。
    ['any', 'any', 'any'].fill('filldata')
    // ['filldata', 'filldata', 'filldata']
    
    
    数组实例的 entries() ,keys()values()
    entries() ,keys()和values() ——用于遍历数组。它们都返回一个遍历器对象
    可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。
    
    for (let index of ['1', '2'].keys()) {
        console.log(index);
    }
    // 0
    // 1
    for (let elem of ['1', '2'].values()) {
        console.log(elem);
    }
    // '1'
    // '2'
    for (let [index, elem] of ['1', '2'].entries()) {
        console.log(index, elem);
    }
    // 0 "1"
    // 1 "2"
    
    
    includes()
    方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。
    [1, 2, 3].includes(2)     // true
    
    数组实例的 flat()
    flat()用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。
    [1, 2, [3, 4]].flat()
    // [1, 2, 3, 4]
    
    
    
    
    7.对象新增方法
    Object.is()
    ES5 比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)
    它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及 + 0等于 - 0
    JavaScript 缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。
    
    Object.is就是用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。
    Object.is('fruit', 'fruit')
    // true
    Object.is({}, {})
    // false
    
    Object.assign()
    用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
    const target = { a: 1 };
    const source1 = { b: 2 };
    const source2 = { c: 3 };
    Object.assign(target, source1, source2);
    target // {a:1, b:2, c:3}
    
    
    Object.keys()
    函数返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。
    var obj = { fruit: 'apple', age: 42 };
    Object.keys(obj)
    // ["fruit", "age"]
    
    Object.values()
    函数返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。
    const obj = { fruit: 'apple', age: 42 };
    Object.values(obj)
    // ["apple", 42]
    
    Object.entries()
    函数返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。
    const obj = { fruit: 'apple', age: 42 };
    Object.entries(obj)
    // [ ["fruit", "apple"], ["age", 42] ]
    
    
    Object.fromEntries()
    函数是Object.entries()的逆操作,用于将一个键值对数组转为对象。
    Object.fromEntries([
        ['fruit', 'apple'],
        ['age', 42]
    ])
    // { fruit: "apple", age: 42 }
    
    
    8.set Map
    
    Set
    它类似于数组,但是成员的值都是唯一的,没有重复的值
    Set本身是一个构造函数,用来生成 Set 数据结构
    const s = new Set();
    [2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
    for (let i of s) {
        console.log(i);
    }
    // 2 3 5 4
    上面代码通过add()方法向 Set 结构加入成员,结果表明 Set 结构不会添加重复的值。
    Set函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。
    Set 实例的属性和方法
    Set.prototype.constructor:构造函数,默认就是Set函数。
    Set.prototype.size:返回Set实例的成员总数。
    Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。
    Set.prototype.add(value) :添加某个值,返回 Set 结构本身。
    Set.prototype.delete(value) :删除某个值,返回一个布尔值,表示删除是否成功。
    Set.prototype.has(value) :返回一个布尔值,表示该值是否为Set的成员。
    Set.prototype.clear() :清除所有成员,没有返回值。
    遍历操作
    Set 结构的实例有四个遍历方法,可以用于遍历成员。
    Set.prototype.keys() :返回键名的遍历器
    Set.prototype.values() :返回键值的遍历器
    Set.prototype.entries() :返回键值对的遍历器
    Set.prototype.forEach() :使用回调函数遍历每个成员
    (1)keys() ,values() ,entries()
    keys方法、values方法、entries方法返回的都是遍历器对象
    由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值)所以keys方法和values方法的行为完全一致
    let set = new Set(['am', 'is', 'are']);
    for (let item of set.keys()) {
        console.log(item);
    }
    // am
    // is
    // are
    for (let item of set.values()) {
        console.log(item);
    }
    // am
    // is
    // are
    for (let item of set.entries()) {
        console.log(item);
    }
    // ["am", "am"]
    // ["is", "is"]
    // ["are", "are"]
    
    
    
    Map
    JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制
    Map类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
    const m = new Map();
    const o = { p: 'Hello World' };
    m.set(o, 'content')
    m.get(o) // "content"
    m.has(o) // true
    m.delete(o) // true
    m.has(o) // false
    上面代码使用 Map 结构的set方法,将对象o当作m的一个键,然后又使用get方法读取这个键,接着使用delete方法删除了这个键。
    实例的属性和操作方法
    Map 结构的实例有以下属性和操作方法。
    (1)size 属性
    size属性返回 Map 结构的成员总数。
    const map = new Map();
    map.set('fruit', true);
    map.set('apple', false);
    map.size // 22)Map.prototype.set(key, value)
    set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。
    const m = new Map();
    m.set('edition', 6)        // 键是字符串3)Map.prototype.get(key)
    get方法读取key对应的键值,如果找不到key,返回undefined。
    const m = new Map();
    m.set(hello, 'Hello ES6!') // 键是函数
    m.get(hello)  // Hello ES6!4)Map.prototype.has(key)
    has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。
    const m = new Map();
    m.set('edition', 6);
    m.has('edition')     // true
    m.has('years')       // false5)Map.prototype.delete(key)
    delete方法删除某个键,返回true。如果删除失败,返回falseconst m = new Map();
    m.set(undefined, 'nah');
    m.has(undefined)     // true
    m.delete(undefined)
    m.has(undefined)       // false6)Map.prototype.clear()
    clear方法清除所有成员,没有返回值。
    let map = new Map();
    map.set('fruit', true);
    map.set('apple', false);
    map.size // 2
    map.clear()
    map.size // 0
    遍历方法
    Map 结构原生提供三个遍历器生成函数和一个遍历方法。
    Map.prototype.keys() :返回键名的遍历器。
    Map.prototype.values() :返回键值的遍历器。
    Map.prototype.entries() :返回所有成员的遍历器。
    Map.prototype.forEach() :遍历 Map 的所有成员。
    需要特别注意的是,Map 的遍历顺序就是插入顺序。
    const map = new Map([
        ['F', 'no'],
        ['T', 'yes'],
    ]);
    for (let key of map.keys()) {
        console.log(key);
    }
    // "F"
    // "T"
    for (let value of map.values()) {
        console.log(value);
    }
    // "no"
    // "yes"
    for (let item of map.entries()) {
        console.log(item[0], item[1]);
    }
    // "F" "no"
    // "T" "yes"
    
    
    9.promise
    Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大
    Promise简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果
    从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理
    
    Promise对象有以下两个特点。
    (1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。
    只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
    (2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。
    只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。
    这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
    有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。
    基本用法
    下面代码创造了一个Promise实例。
    
    const promise = new Promise(function (resolve, reject) {
        // ... some code
    
        if (/* 异步操作成功 */) {
            resolve(value);
        } else {
            reject(error);
        }
    });
    Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署
    resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去
    reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去
    
    Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
    promise.then(function (value) {
        // success
    }, function (error) {
        // failure
    });
    then方法可以接受两个回调函数作为参数
    第一个回调函数是Promise对象的状态变为resolved时调用
    第二个回调函数是Promise对象的状态变为rejected时调用。
    其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。
    
    下面是一个用户登录的Promise对象的简单例子。
    function checkStatus(response) {
        return new Promise((resolve, reject) => {
            if (response.status === 200) {
                if (response.data.code === 401 && !hasConfirm) {
                    hasConfirm = true;
                    MessageBox.confirm('登陆超时是否重新登录?', '提示', {
                        distinguishCancelAndClose: true,
                        confirmButtonText: '确定',
                        cancelButtonText: '取消',
                        type: 'warning'
                    }).then(() => {
                        sessionStorage.removeItem('token');
                        hasConfirm = false;
                        router.push({ path: '/login' });
                        window.location.reload();
                    }).catch(() => {
                        hasConfirm = false;
                    });
                }
                if (response.data.code) {
                    if (response.data.code == 200) {
                        resolve(response.data);
                    } else if (response.data.code == 401) { } else {
                        reject(response.data);
                    }
                } else {
                    if (response.config.responseType == 'blob') {
                        resolve(response);
                    } else {
                        resolve(response.data);
                    }
                }
            } else {
                reject(response.data);
            }
        });
    }
    
    post(url, data) {
        let obj = data;
    
        return axios({
            method: 'post',
            url,
            data: qs.stringify(obj),
            headers: {
                'X-Requested-With': 'XMLHttpRequest',
                'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                'Authorization': sessionStorage.getItem('token')
            }
        }).then(checkStatus);
    }
    
    
    10.迭代器
    遍历器一种接口,为各种不同的数据结构提供统一的访问机制。通过它可以完成遍历操作
    Iterator 的作用有三个:
    一是为各种数据结构,提供一个统一的、简便的访问接口;
    二是使得数据结构的成员能够按某种次序排列;
    三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。
    Iterator 的遍历过程是这样的。
    (1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
    (2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
    (3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
    (4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。
    每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。
    其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
    
    下面是一个模拟next方法返回值的例子。
    var it = makeIterator(['a', 'b']);
    it.next() // { value: "a", done: false }
    it.next() // { value: "b", done: false }
    it.next() // { value: undefined, done: true }
    function makeIterator(array) {
        var nextIndex = 0;
        return {
            next: function () {
                return nextIndex < array.length ?
                    { value: array[nextIndex++], done: false } :
                    { value: undefined, done: true };
            }
        };
    }
    上面代码定义了一个makeIterator函数,它是一个遍历器生成函数,作用就是返回一个遍历器对象。
    对数组['a', 'b']执行这个函数,就会返回该数组的遍历器对象(即指针对象)it。
    指针对象的next方法,用来移动指针。开始时,指针指向数组的开始位置。然后,每次调用next方法,指针就会指向数组的下一个成员。
    第一次调用,指向a;第二次调用,指向b。
    next方法返回一个对象,表示当前数据成员的信息。这个对象具有value和done两个属性,value属性返回当前位置的成员,
    done属性是一个布尔值,表示遍历是否结束,即是否还有必要再一次调用next方法。
    总之,调用指针对象的next方法,就可以遍历事先给定的数据结构。
    对于遍历器对象来说,done: false和value: undefined属性都是可以省略的,因此上面的makeIterator函数可以简写成下面的形式。
    
    function makeIterator(array) {
        var nextIndex = 0;
        return {
            next: function () {
                return nextIndex < array.length ?
                    { value: array[nextIndex++] } :
                    { done: true };
            }
        };
    }
    
    
    11.async
    async 函数使得异步操作变得更加方便。
    await等待右侧表达式的结果,这个结果是promise对象或者其他值。
    如果它等到的不是一个 promise 对象,那 await 表达式的运算结果就是它等到的东西。
    如果它等到的是一个 promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 promise 对象 resolve,
    然后得到 resolve 的值,作为 await 表达式的运算结果。
    function test() {
        return new Promise(resolve => {
            setTimeout(() => resolve("test"), 2000);
        });
    }
    const result = await test();
    console.log(result);
    console.log('end')
    
    
    
    
    
    12.class
    JavaScript 语言中,生成实例对象的传统方法是通过构造函数。下面是一个例子。
    function People(age, sex) {
        this.age = x;
        this.sex = y;
    }
    People.prototype.toString = function () {
        return '(' + this.age + ', ' + this.sex + ')';
    };
    
    var p = new People(1, 2);
    ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。
    class People {
        constructor(age, sex) {
            this.age = x;
            this.sex = y;
        }
    
        toString() {
            return '(' + this.age + ', ' + this.sex + ')';
        }
    }
    上面代码定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。
    也就是说,ES5 的构造函数People,对应 ES6 的People类的构造方法。
    
    People类除了构造方法,还定义了一个toString方法。注意,定义“类”的方法的时候,前面不需要加上function这个关键字,
    直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。
    
    class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
    
    class People {
    }
    
    class ColorPeople extends People {
    }
    上面代码定义了一个ColorPeople类,该类通过extends关键字,继承了People类的所有属性和方法。
    但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个People类。下面,我们在ColorPeople内部加上代码。
    
    class ColorPeople extends People {
        constructor(x, y, color) {
            super(age, sex); // 调用父类的constructor(age, sex)
            this.color = color;
        }
        toString() {
            return this.color + ' ' + super.toString(); // 调用父类的toString()
        }
    }
    
    13.模块
    JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。
    
    在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。
    
    // CommonJS模块
    let { stat, exists, readfile } = require('fs');
    
    // 等同于
    let _fs = require('fs');
    let stat = _fs.stat;
    let exists = _fs.exists;
    let readfile = _fs.readfile;
    
    ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。
    
    // ES6模块
    import { stat, exists, readFile } from 'fs';
    上面代码的实质是从fs模块加载 3 个方法,其他方法不加载。
    这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。
    当然,这也导致了没法引用 ES6 模块本身,因为它不是对象。
    
    由于 ES6 模块是编译时加载,使得静态分析成为可能。有了它,就能进一步拓宽 JavaScript 的语法,比如引入宏(macro)和类型检验(type system)
    这些只能靠静态分析实现的功能。
    除了静态加载带来的各种好处,ES6 模块还有以下好处。
        不再需要UMD模块格式了,将来服务器和浏览器都会支持 ES6 模块格式。目前,通过各种工具库,其实已经做到了这一点。
        将来浏览器的新 API 就能用模块格式提供,不再必须做成全局变量或者navigator对象的属性。
        不再需要对象作为命名空间(比如Math对象),未来这些功能可以通过模块提供。
    
    
    展开全文
  • ES6

    2018-09-20 14:33:24
    ES6 ES6 一、 变量赋值与解构 二、- 字符串拓展 三、- 正则拓展 四、- 数值的拓展 五、- 函数的扩展 五、- 数组的扩展 六、- 对象的扩展 七、- Symbol类型:表示独一无二的值 八、 Set 和 Map ...

    ES6

    一、 变量赋值与解构

    • 默认值赋值注意点:
      • 只有当一个数组成员严格等于undefined,默认值才会生效。
          let [x = 1] = [undefined];
          x // 1
          
          let [x = 1] = [null];
          x // null
      
      • 默认值可以引用解构赋值的其他变量,但该变量必须已经声明。
          let [x = 1, y = x] = [];     // x=1; y=1
          let [x = 1, y = x] = [2];    // x=2; y=2
          let [x = 1, y = x] = [1, 2]; // x=1; y=2
          let [x = y, y = 1] = [];     // ReferenceError: y is not defined
      

    • 变量的解构注意点:

      • 由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构。
          let arr = [1, 2, 3];
          let {0 : first, [arr.length - 1] : last} = arr;
          first // 1
          last // 3
      
      • 解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。
          let { prop: x } = undefined; // TypeError
          let { prop: y } = null; // TypeError
      
      • 为了区分模式,还是表达式 --> 只要有可能导致解构的歧义,就不得使用圆括号。
          //变量申明
          let [(a)] = [1];
          let {x: (c)} = {};
          
          // 函数参数
          function f([(z)]) { return z; }
          function f([z,(x)]) { return x; }
          
          //赋值语句的模式部分
          ({ p: a }) = { p: 42 };
          ([a]) = [5];
          
          //可以使用(赋值语句的非模式部分)
          [(b)] = [3]; // 正确
          ({ p: (d) } = {}); // 正确
          [(parseInt.prop)] = [3]; // 正确
      
       > 与`for...of`配合 获取键名和键值就非常方便。
      

    二、- 字符串拓展

    1. 详见阮一峰老师网站-String



    三、- 正则拓展

    1. 详见阮一峰老师网站-RegExp
      • u修饰符,含义为“Unicode 模式”

      • y 修饰符“粘连”(sticky)修饰符:确保匹配必须从剩余的第一个位置开始

      • ‘后行断言’

        • 先行断言:x只有在y前面才匹配,必须写成/x(?=y)/
          比如:只匹配百分号之前的数字,要写成/\d+(?=%)/

        • 先行否定断言:x只有不在y前面才匹配,必须写成/x(?!y)/

        • 后行断言:x只有在y后面才匹配,必须写成/(?<=y)x/
          如:只匹配美元符号之后的数字,要写成/(?<=\$)\d+/

        • 后行否定断言:x只有不在y后面才匹配,必须写成/(?<!y)x/

        后行断言与正常情况顺序是相反的,为“先右后左”的执行顺序

      • 具名组匹配:允许为每一个组匹配指定一个名字;
        引用某个“具名组匹配” \k<组名>或者\1
        具名组匹配在圆括号内部,模式的头部添加“问号 + 尖括号 + 组名”(?<year>),然后就可以在exec方法返回结果的groups属性上引用该组名。

            const REG = /^(?<word>[a-z]+)!\k<word>!\1$/;
            REG.test('abc!abc!abc') // true
            REG.test('abc!abc!ab') // false
        



    四、- 数值的拓展

    1. 二进制和八进制表示法(前缀0b | 0o)
    2. Number.isFinite(), Number.isNaN()
    3. Number.parseInt(), Number.parseFloat()
    4. Number.isInteger()
    5. Number.EPSILON(最小精度) 2 ** -52
    6. 安全整数和 Number.isSafeInteger(): 判断是否在安全值范围
    7. Math 对象的扩展
    8. 指数运算符**



    五、- 函数的扩展

    1. 函数参数的默认值

    2. rest 参数 …props

    3. 严格模式

    4. name 属性

    5. 箭头函数 =>this引用的外层函数
      注意点:

      • (1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
      • (2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
      • (3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
      • (4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
    6. 双冒号运算符

       foo::bar;
       // 等同于
       bar.bind(foo);
       
       var method = obj::obj.foo;
      // 等同于
      var method = ::obj.foo;
      
    7. 尾调用:(指某个函数的最后一步是调用另一个函数)优化

      • 只保留内层调用

      注意点:只有不再用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧,否则就无法进行“尾调用优化”。

    8. 尾递归:尾调用自身

      //计算n的阶乘,最多需要保存n个调用记录,复杂度 O(n) 。
       function factorial(n) {
          if (n === 1) return 1;
          return n * factorial(n - 1);
      }
      
      factorial(5) // 120
      
      //尾递归优化,只保留一个调用记录,复杂度 O(1) 。
      function factorial(n, total = 1) {
          if (n === 1) return total;
          return factorial(n - 1, n * total);
      }
      
      factorial(5) // 120
      

      tips:把所有用到的内部变量改写成函数的参数



    六、- 数组的扩展

    1. 扩展运算符(...[])的应用

      • 转成参数序列

      • 复制数组

        const a1 = [1, 2];
        const a2 = a1.concat(); //es5
        
        const a3 = [...a1];     //es6
        const [...a4] = a1;
        
      • 合并数组 [...arr1, ...arr2, ...arr3] (浅拷贝)

      • 解构赋值 const [first, ...rest] = [1, 2, 3, 4, 5]

      • 正确返回字符长度 [...str].length

        'x\uD83D\uDE80y'.length // 4
        [...'x\uD83D\uDE80y'].length // 3
        
      • 实现 Iterator 接口的对象.
        任何 Iterator 接口的对象,都可以用扩展运算符转为真正的数组。
        对于那些没有部署 Iterator接口的类似数组的对象,使用Array.from(arrayLike),实际上任何有length属性的对象,都可以通过Array.from方法转为数组。

    2. Array.from():

      • 用于将两类对象转为真正的数组:类似数组的对象和可遍历的对象(包括 ES6 新增的数据结构 Set 和 Map)。
      • Array.from还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。
         Array.from(arrayLike, x => x * x);
         // 等同于
         Array.from(arrayLike).map(x => x * x);
      
         Array.from([1, 2, 3], (x) => x * x)
         // [1, 4, 9]
      
    3. Array.of():

    - 用于将一组值,转换为数组。
    - 为了弥补构造函数`Array()`的不足
    
        ```
            //参数个数不一样行为不同
            Array() // []
            Array(3) // [, , ,]
            Array(3, 11, 8) // [3, 11, 8]
        ```
        `Array.of`总是返回参数值组成的数组。如果没有参数,就返回一个空数组。
    
    1. 数组实例的 copyWithin()

      • 将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。。
      • Array.prototype.copyWithin(target, start = 0, end = this.length)
    2. 数组实例的 fill()

      • 可用于填充数组,数组中已有的元素,会被全部抹去。

      • fill方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。

        ['a', 'b', 'c', 'd'].fill(7, 1, 3)
        // ['a', 7, 7, 'd']
        
        //> 注意,如果填充的类型为对象,那么被赋值的是同一个内存地址的对象,而不是深拷贝对象。
        let arr = new Array(3).fill([]);
        arr[0].push(5);
        // arr = [[5], [5], [5]]
        
    3. 数组实例的 entries()keys()values()

      • for...of循环进行遍历,keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。

        for (let index of ['a', 'b'].keys()) {
            console.log(index);
        }
        // 0
        // 1
        
        for (let elem of ['a', 'b'].values()) {
          console.log(elem);
        }
        // 'a'
        // 'b'
        
        for (let [index, elem] of ['a', 'b'].entries()) {
          console.log(index, elem);
        }1
        // 0 "a"
        // 1 "b"
        
    4. 数组实例的 includes()

      • 表示某个数组是否包含给定的值(第二个参数表示搜索的起始位置),与字符串的includes方法类似
         [1, 2, 3].includes(3, 3)   // false
         [1, 2, 3].includes(3, -1)  // true
         [1, 2, [3]].includes([3])  // false
         [NaN].includes(NaN)        // true
      
    5. 数组实例的 flat()flatMap() —Chrome无此方法(2018/8/21)

      • flat()用于将嵌套的数组“拉平”,变成一维的数组。参数:想要拉平的层数,默认为1。

        [1, 2, [3, [4, 5]]].flat()
        // [1, 2, 3, [4, 5]]
        
        //如果原数组有空位,flat()方法会跳过空位。
        [1, 2, , 4, 5].flat()
        // [1, 2, 4, 5]
        
        [1, [2, [3]]].flat(Infinity) //Infinity:不管有多少层嵌套,都要转成一维数组
        // [1, 2, 3]
        
      • flatMap() 方法对原数组的每个成员执行一个函数,然后对返回值组成的数组执行flat()方法。该方法返回一个新数组,不改变原数组。传入函数(类似map),只能展开一层

    6. 数组的空位的处理

      • ES5:

        • forEach(), filter(), reduce(), every() 和some()都会跳过空位。
        • map()会跳过空位,但会保留这个值
        • join()和toString()会将空位视为undefined,而undefined和null会被处理成空字符串。
      • ES6:

        • 明确将空位转为undefined

          [...['a',,'b']]
          // [ "a", undefined, "b" ]
          



    七、- 对象的扩展

    1. 简写{foo} === {foo: foo}

    2. Object.is():

          +0 === -0 //true
          NaN === NaN // false
          
          Object.is(+0, -0) // false
          Object.is(NaN, NaN) // true
      
    3. 对象详情



    八、Symbol类型:表示独一无二的值

    1. Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的。
    2. Symbol 值不能与其他类型的值进行运算,会报错。
    3. Symbol 值可以显式转为字符串|布尔值但不能转为数值。
    4. Symbol 作为属性名,该属性不会出现在for...infor...of循环中,也不会被Object.keys()Object.getOwnPropertyNames()JSON.stringify()返回。但是,它也不是私有属性,有一个Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名。



    九、 Set 和 Map 数据结构

    1. Set: 它类似于数组,但是成员的值都是唯一的,没有重复的值。
       const set = new Set([1, 2, 3, 4, 4, 4, 4]);
       [...set]
       // [1, 2, 3, 4]
       
       // 去除数组的重复成员
       [...new Set(array)]
       
       //在 Set 内部,两个NaN是相等。
       //另外,两个对象总是不相等的。
    
    - add(value):添加某个值,返回 Set 结构本身。
    - delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
    - has(value):返回一个布尔值,表示该值是否为Set的成员。
    - clear():清除所有成员,没有返回值。
    
    1. WeakSet :与 Set 类似,但 WeakSet 的成员只能是对象,而不能是其他类型的值。
      没有size属性,没有办法遍历它的成员。

    2. Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。这就解决了同名属性碰撞(clash)的问题,我们扩展别人的库的时候,如果使用对象作为键名,就不用担心自己的属性与原作者的属性同名。

          const map = new Map();
          const k1 = ['a'];
          const k2 = ['a'];
          
          map
          .set(k1, 111)
          .set(k2, 222);
          
          map.get(k1) // 111
          map.get(k2) // 222
      
    3. WeakMap :与Map结构类似,也是用于生成键值对的集合。

      • 区别:
        • WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。

        • WeakMap的键名所指向的对象,不计入垃圾回收机制。

          const wm = new WeakMap();
          
          const element = document.getElementById('example');
          
          wm.set(element, 'some information');
          wm.get(element) // "some information"
          /*
          先新建一个 Weakmap 实例。然后,将一个 DOM 节点作为键名存入该实例,
          并将一些附加信息作为键值,一起存放在 WeakMap 里面。这时,WeakMap 里面对element
          的引用就是弱引用,不会被计入垃圾回收机制即DOM节点对象的引用计数是1,而不是2。
          可以有效防止内存泄漏。
          (WeakMap 的另一个用处是部署私有属性。如果删除实例,它们也就随之消失)
          */
          



    十、 Proxy

    1. 含义:在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。(代理器)

    2. new Proxy(target, handler)

    3. Proxy支持的拦截操作

      1. get(target, propKey, receiver):拦截对象属性的读取,比如proxy.fooproxy['foo']
      2. set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = vproxy['foo'] = v,返回一个布尔值。
      3. has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
      4. deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。
      5. ownKeys(target):拦截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
      6. getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
      7. defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)Object.defineProperties(proxy, propDescs),返回一个布尔值。
      8. preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
      9. getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
      10. isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
      11. setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
      12. apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)proxy.call(object, ...args)proxy.apply(...)
      13. construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)



    十一、 Reflect

    1. 从Reflect对象上可以拿到语言内部的方法。

    2. 13个静态方法

      • Reflect.apply(target, thisArg, args)

      • Reflect.construct(target, args)

      • Reflect.get(target, name, receiver)

        const myObject = {
          foo: 1,
          bar: 2,
          get baz() {
            return this.foo + this.bar;
          },
        }
        console.log(Reflect.get(myObject, 'foo')) // 1
        console.log(Reflect.get(myObject, 'bar')) // 2
        console.log(Reflect.get(myObject, 'baz')) // 3
        
      • Reflect.set(target, name, value, receiver)

        myObject.foo // 1
        
        Reflect.set(myObject, 'foo', 2);
        myObject.foo // 2
        
      • Reflect.defineProperty(target, name, desc)

        // 新写法
        Reflect.defineProperty(MyDate, 'now', {
          value: new Date()
        })
        
        console.log(MyDate.now)
        
      • Reflect.deleteProperty(target, name)

      • Reflect.has(target, name)

      • Reflect.ownKeys(target)

      • Reflect.isExtensible(target)

      • Reflect.preventExtensions(target)

      • Reflect.getOwnPropertyDescriptor(target, name)

        const myObject = {};
        Reflect.defineProperty(myObject, 'hidden', {
          value: true,
          writable:true,
        });
        
        const theDescriptor = Reflect.getOwnPropertyDescriptor(myObject,'hidden');
        console.log(theDescriptor)
        //{value: true, writable: true, enumerable: false, configurable: false}
        myObject.hidden = 1
        console.log(myObject) //{hidden: 1}
        
      • Reflect.getPrototypeOf(target)

      • Reflect.setPrototypeOf(target, prototype)

    3. 实例:使用 Proxy 实现观察者模式

      const queuedObservers = new Set();
      
      const observe = fn => queuedObservers.add(fn);
      const observable = obj => new Proxy(obj, {set});
      
      function set(target, key, value, receiver) {
        const result = Reflect.set(target, key, value, receiver);
        queuedObservers.forEach(observer => observer());
        return result;
      }
      
      const person = observable({
        name: '张三',
      });
      function print() {
        console.log(`${person.name}`)
      }
      
      observe(print);
      person.name = '李四';  //李四
      person.name = '33';     //33
      
      

      上面代码中,先定义了一个Set集合,所有观察者函数都放进这个集合。然后,observable函数返回原始对象的代理,拦截赋值操作。拦截函数set之中,会自动执行所有观察者—这跟vue很相似



    十二、 Promise 对象

    1. 含义:Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。

    2. Promise.prototype.then()
      then方法返回的是一个新的Promise实例。因此可以采用链式写法

    3. Promise.prototype.catch()
      .then(null, rejection)的别名,一般建议总是使用catch方法。catch方法可以捕获前面then方法执行中的错误,也更接近同步的写法(try/catch)。

    如果没有使用catch方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码

    1. Promise.prototype.finally()
      不管 Promise 对象最后状态如何,都会执行的操作。

      promise
      .finally(() => {
        // 语句
      });
      
      // 等同于
      promise
      .then(
        result => {
          // ...
          return result;
        },
        error => {
          //...
          throw error;
        }
      )
      
    2. Promise.all()
      const p = Promise.all([p1, p2, p3]);

      注意:

      • Promise.all方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。)
      • 只有所有的状态都变成fulfilled,返回值组成一个数组,传递给回调函数,
        或者有一个变为rejected,被reject的实例的返回值才会继续传递给回调函数。
    3. Promise.race()
      const p = Promise.race([p1, p2, p3]);
      只要有一个状态改变就。。。

      //当接口五秒内未返回就报超时错误
      const p = Promise.race([
        fetch('/resource-that-may-take-a-while'),
        new Promise(function (resolve, reject) {
          setTimeout(() => reject(new Error('request timeout')), 5000)
        })
      ]);
      
      p
      .then(console.log)
      .catch(console.error);
      
    4. Promise.resolve()

      Promise.resolve('foo')
      // 等价于(可以将现有对象转成Promise对象)
      new Promise(resolve => resolve('foo'))
      
      • 参数是个Promise 实例:不做修改直接返回这个实例
      • 参数是个具有then方法的对象:先转Promise然后立即执行then方法
      • 参数不是具有then方法的对象,或根本就不是对象:返回状态为resolved的Promise对象
        不带有任何参数:直接返回一个resolved状态的 Promise 对象。
    5. Promise.reject()
      Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。

    6. Promise.try()
      不知道或者不想区分,函数f是同步函数还是异步操作,但是想用 Promise 来处理它时。

      解决方案1 :

      const f = () => console.log('now');
      (async () => f())()
      console.log('next')
      // now
      // next
      

      解决方案2 :

      const f = () => console.log('now');
      (
        () => new Promise(
          resolve => resolve(f())
        )
      )();
      console.log('next');
      // now
      // next
      


    十三、Iterator 和 for…of 循环

    ①. Iterator

    1. 凡是部署了Symbol.iterator属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器对象。
      原生具备Iterator接口的数据结构 (可以用for…of遍历它们):

      • Array
      • Map
      • Set
      • String
      • TypedArray
      • 函数的 arguments 对象
      • NodeList 对象

    2. 默认调用Symbol.iterator方法的情况

      • 对数组和 Set 结构进行解构赋值时
      • 使用扩展运算符(…)
      • yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。
      const generator = function* () {
        yield* [1,2];
      };
      
      var iterator = generator();
      
      iterator.next() // { value: 1, done: false }
      iterator.next() // { value: 2, done: false }
      iterator.next() // { value: undefined, done: true }
      
      • 数组的遍历会调用遍历器接口
        • for…of
        • Array.from()
        • Map(), Set(), WeakMap(), WeakSet()(比如new Map([[‘a’,1],[‘b’,2]]))
        • Promise.all()
        • Promise.race()
    3. 字符串是一个类似数组的对象,也原生具有 Iterator 接口。

    4. 遍历器对象的 return()

      可以触发return的情况

      // 情况一:break
      for (let line of readLinesSync(fileName)) {
        console.log(line);
        break;
      }
      
      // 情况二:Error
      for (let line of readLinesSync(fileName)) {
        console.log(line);
        throw new Error();
      }
      
      • 情况一输出文件的第一行以后,就会执行return方法,关闭这个文件;
      • 情况二会在执行return方法关闭文件之后,再抛出错误。

    #### ②. for...of 循环 1. `for...of`循环内部调用的是数据结构的`Symbol.iterator`方法。 2. `for...in`循环读取键名,`for...of`循环读取键值。 `for...of`循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性。 ``` let arr = [3, 5, 7]; arr.foo = 'hello';
    for (let i in arr) {
      console.log(i); // "0", "1", "2", "foo"
    }
    
    for (let i of arr) {
      console.log(i); //  "3", "5", "7"
    }
    ```
    

    Set和Map

    1. Set 结构遍历时,返回的是一个值,而 Map 结构遍历时,返回的是一个数组([k,v])

    其他

    1. 对于没有Iterator接口的类数组对象:使用Array.from方法将其转为数组。



    十四、Generator 函数的语法

    1. Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的Symbol.iterator属性,从而使得该对象具有 Iterator 接口。
       const myIterable = {}
       myIterable[Symbol.iterator] = function* () {
         yield 1
         yield 2
         yield 3
       };
       
       console.log([...myIterable])  // [1, 2, 3]
    

    2. 注意: - `yield`表达式只能用在 Generator 函数里面,用在其他地方都会报错。 - `yield`表达式如果用在另一个表达式之中,必须放在圆括号里面。 - `yield`表达式用作函数参数或放在赋值表达式的右边,可以不加括号。

    1. next 方法的参数

      • next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值
        也就是说:可以在 Generator函数运行的不同阶段,从外部向内部注入不同的值,从而调整函数行为。
        • 注意: 由于next方法的参数表示上一个yield表达式的返回值,所以在第一次使用next方法时,传递参数是无效的

    2. Generator.prototype.throw():

      • 只要 Generator 函数内部部署了try…catch代码块,那么遍历器的throw方法抛出的错误,不影响下一次遍历。
      • 一旦 Generator 执行过程中抛出错误,且没有被内部捕获,就不会再执行下去了。

    3. Generator.prototype.return():

      • 可以返回给定的值,并且终结遍历 Generator 函数。

    4. yield* 表达式:为了说明yield表达式后面跟的是一个遍历器对象时加*

    5. Generator 函数的this:

      • Generator 函数总是返回遍历器对象而不是this
      function* gen() {
        this.a = 1;
        yield this.b = 2;
        yield this.c = 3;
      }
      
      function F() {
        return gen.call(gen.prototype);//<--
      }
      
      var f = new F();
      
      f.next();  // Object {value: 2, done: false}
      f.next();  // Object {value: 3, done: false}
      f.next();  // Object {value: undefined, done: true}
      
      f.a // 1
      f.b // 2
      f.c // 3
      

    6. Generator

      • 与状态机:完美管理状态
      • 协程:既可以用单线程实现,也可以用多线程实现。前者是一种特殊的子例程,后者是一种特殊的线程。
      • 传统的“子例程”(subroutine)采用堆栈式“后进先出”的执行方式,只有当调用的子函数完全执行完毕,才会结束执行父函数。
      • 与普通线程差异:
        • 同一时间可以有多个线程处于运行状态,但是运行的协程只能有一个,其他协程都处于暂停状态。
        • 普通的线程是抢先式的,到底哪个线程优先得到资源,必须由运行环境决定,但是协程是合作式的,执行权由协程自己分配。



    十五、Generator 函数的异步应用

    1. 传统方法

      • 回调函数 ->(callback hell)强耦合

      • 事件监听

      • 发布/订阅

      • Promise对象 ->代码冗余,原来的任务被Promise包装了一下,一堆then,原来的语义变得很不清楚。

    2. Thunk 函数:是“传名调用”的一种实现策略,用来替换某个表达式。
      /JavaScript 语言是传值调用,以下是个自执行器(thunkify、co模块、)

      //实现自执行
      

    function thunkify(fn) {
    return function() {
    var args = new Array(arguments.length);
    var ctx = this;

        for (var i = 0; i < args.length; ++i) {
          args[i] = arguments[i];
        }
    
        return function (done) {
          var called;
          args.push(function () {
            if (called) return;
            called = true;
            done.apply(null, arguments);
          });
    
          try {
            fn.apply(ctx, args);
          } catch (err) {
            done(err);
          }
        }
      }
    }   
       
     const ft = (n,cb) =>cb(n)
     const CB = thunkify(ft)  
        
        //实现自执行
        function run(fn) {
          var gen = fn()
        
          function next(err, data) {
            var result = gen.next(data)
            console.log(result)
            if (result.done) return;
            result.value(next)
          }
        
          next()
        }
        
    
        const g = function* (){
          var f1 = yield CB('aaaaaa')
          var f2 = yield CB('bbbbbb')
          // ...
          var fn = yield CB('nnnnnn')
        }
        
        run(g)
    ```
    <br/>
    
    1. CO模块

      1. 原理:Generator 就是一个异步操作的容器。它的自动执行需要一种机制,当异步操作有了结果,能够自动交回执行权。以下两种方法可以做到这一点:
        • 回调函数。将异步操作包装成 Thunk 函数,在回调函数里面交回执行权。
        • Promise 对象。将异步操作包装成 Promise 对象,用then方法交回执行权。

      2. 基于 Promise 对象的自动执行
      function run(gen){
        var g = gen();
      
        function next(data){
          var result = g.next(data)
          console.log(result)
          if (result.done) return result.value
          result.value.then(function(data){
            next(data);
          });
        }
      
        next();
      }
      
      //需要返回promise 然后链式自执行
      const CO = (data) => new Promise((resolve)=> resolve(data))
      const g = function* (){
        var f1 = yield CO('aaaaaa')
        var f2 = yield CO('bbbbbb')
        // ...
        var fn = yield CO('nnnnnn')
      }
      
      run(g)
      
      1. 处理并发的异步操作
      • co 支持并发的异步操作,即允许某些操作同时进行,等到它们全部完成,才进行下一步。这时,要把并发的操作都放在数组或对象里面,跟在yield语句后面。
      1. 实例:处理 Stream


    十六、async 函数

    1. Generator 函数的语法糖

      • 改进
      • (1)async函数自带执行器。
      • (2)更广的适用性
        co模块约定,yield命令后面只能是Thunk函数或Promise对象,而async函数的await命令后面,可以是Promise对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)
      • (3)返回值是 Promise
        Generator 函数的返回值是 Iterator 对象

    2. 注意点

      1. async函数内部return语句返回的值,会成为then方法回调函数的参数。
      2. async函数内部抛出错误,会导致返回的Promise对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。
      3. 只要一个await语句后面的Promise变为reject,那么整个async函数都会中断执行。 => 想继续执行 可以吧代码放在try...catch中。
      4. await命令只能用在async函数之中,如果用在普通函数,就会报错。
      • forEach/Map中使用async也会出问题 :
      // scope B
      array.forEach(async function(item) { // scope A
       await wait(1000);
      });
      /*
      `Scope B` 部分的代码并不会等待 `Scope A` 中的 `async/await` 执行完后继续执行后面的代码,相反,他会立刻执行后面的代码
      */
      
      
      • 正确的写法是采用for循环、

    3. 并发执行

      // 写法一
      let [foo, bar] = await Promise.all([getFoo(), getBar()]);
      
      // 写法二
      let fooPromise = getFoo();
      let barPromise = getBar();
      let foo = await fooPromise;
      let bar = await barPromise;
      
    4. for await...of:遍历异步的 Iterator 接口。

      for await (const chunk of readStream) {
          console.log('>>> '+chunk);
      }
      
    5. 异步 Generator 函数

      • 在头部加async 标志,此时即可返回一个Promise



    十七、Class 的基本语法

    1. constructor 方法
      • 通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。
      • constructor方法默认返回实例对象(即this),完全可以指定返回另外一个对象。

    2. class Foo {}不存在声明提升

    3. 私有方法和私有属性
      • 函数名加下划线(仅为命名方法,外部仍可调用)
      • 将私有方法移出模块,因为模块内部的所有方法都是对外可见的。
      class Widget {
        foo (baz) {
          bar.call(this, baz);
        }
        // ...
      }
      
      function bar(baz) {
        return this.snaf = baz;
      }
      
      • 利用Symbol值的唯一性,将私有方法的名字命名为一个Symbol值。
      const bar = Symbol('bar')
      const snaf = Symbol('snaf')
      
      export default class myClass{
        foo(baz) {
          this[bar](baz);
        }
      
        // 私有方法
        [bar](baz) {
          return this[snaf] = baz;
        }
        // ...
      };
      

    4. this 的指向 - 当类的方法中有this使用且方法被提取出来单独使用时this会指向方法运行环境导致意料之外的错误 1. 可以在构造函数中绑定`this.printName = this.printName.bind(this)` 2. 箭头函数: ``` constructor() { this.printName = (name = 'there') => { this.print(`Hello ${name}`) } }
    ```
    3. 使用Proxy,获取方法的时候,自动绑定this
    ```
    function selfish (target) {
      const cache = new WeakMap();
      const handler = {
        get (target, key) {
          const value = Reflect.get(target, key);
          if (typeof value !== 'function') {
            return value;
          }
          if (!cache.has(value)) {
            cache.set(value, value.bind(target));
          }
          return cache.get(value);
        }
      };
      const proxy = new Proxy(target, handler);
      return proxy;
    }
    
    const logger = selfish(new Logger());
    ```
    <br/>
    - Class 的取值函数(getter)和存值函数(setter)
    ```
    class MyClass {
      constructor() {
        // ...
      }
      get prop() {
        return 'getter';
      }
      set prop(value) {
        console.log('setter: '+value);
      }
    }
    
    let inst = new MyClass();
    
    inst.prop = 123;
    // setter: 123
    
    inst.prop
    // 'getter'
    
    ```
    
    </br>
    - Class 的静态方法
        在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用
    



    十八、Class 的继承

    1. 子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。 =>constructor不写默认 有super(...args)

    2. Object.getPrototypeOf:从子类上获取父类

      //可以使用这个方法判断,一个类是否继承了另一个类。
      Object.getPrototypeOf(ColorPoint) === Point
      // true
      
      
    3. super:

      • super在B的构造函数中,指向A.prototype,所以super可以拿到父类原型对象上的实例方法
      • 如果super作为对象,用在静态方法之中,这时super将指向父类,而不是父类的原型对象。
    4. 原生构造函数的继承

      • Boolean()
        Number()
        String()
        Array()
        Date()
        Function()
        RegExp()
        Error()
        Object()

      原生构造函数会忽略apply方法传入的this,也就是说,原生构造函数的this无法绑定,导致拿不到内部属性。
      extends关键字不仅可以用来继承类,还可以用来继承原生的构造函数!

    5. Mixin 模式的实现(多个对象合成一个新的对象)

      function mix(...mixins) {
        class Mix {}
      
        for (let mixin of mixins) {
          copyProperties(Mix.prototype, mixin); // 拷贝实例属性
          copyProperties(Mix.prototype, Reflect.getPrototypeOf(mixin)); // 拷贝原型属性
        }
      
        return Mix;
      }
      
      function copyProperties(target, source) {
        for (let key of Reflect.ownKeys(source)) {
          if ( key !== "constructor"
            && key !== "prototype"
            && key !== "name"
          ) {
            let desc = Object.getOwnPropertyDescriptor(source, key);
            Object.defineProperty(target, key, desc);
          }
        }
      }
      



    十九、修饰器(提案)

    1. 修饰器只能用于类和类的方法,不能用于函数,因为存在函数提升
    2. 详见>>ES6-修饰器



    二十、Module 的语法

    1. 注意点
      • import在静态解析阶段执行,所以它是一个模块之中最早执行的。
      • import是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。
      // 报错
      import { 'f' + 'oo' } from 'my_module';
      
      // 报错
      let module = 'my_module';
      import { foo } from module;
      
      // 报错
      if (x === 1) {
        import { foo } from 'module1';
      } else {
        import { foo } from 'module2';
      }
      //在静态分析阶段,这些语法都是没法得到值的。
      
    2. export default命令其实只是输出一个叫做default的变量
    3. export 与 import 的复合写法
      //foo和bar实际上并没有被导入当前模块,只是相当于对外转发了这两个接口
      export { foo, bar } from 'my_module'
      //>可以用来合并一些模块
      
      
    4. 提案:import()函数 => 用来动态加载模块 返回Promise



    二十、Module 的加载实现

    1. 浏览器加载

      • 传统方法
      • defer :“渲染完再执行” , 多个defer脚本,会按照它们在页面出现的顺序加载。
      • async :“下载完就执行” , 多个async脚本,不能保证加载顺序。
      • 加载 ES6 模块
      • <script type="module" src="./...js"></script>:此时行为与defer相同

    2. ES6 模块与 CommonJS 模块的差异

      • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用
      • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

    3. Node 加载

      • Node 有自己的 CommonJS 模块格式,与 ES6 模块格式是不兼容的

      • Node 要求 ES6 模块采用.mjs后缀文件名。(新版)
      $ node --experimental-modules my-app.mjs
      
      • 内部变量差异
      • ES6 模块之中,顶层的this指向undefined;CommonJS 模块的顶层this指向当前模块。
      • ES6不存在这些顶层变量:
        1. arguments
        2. require
        3. module
        4. exports
        5. __filename
        6. __dirname

      • ES6 模块加载 CommonJS 模块
        • Node 的import命令加载 CommonJS 模块,Node 会自动将module.exports属性,当作模块的默认输出,即等同于{ default: module.exports }
        • import命令加载 CommonJS 模块时,不允许采用解构的写法,改为整体输入

      • CommonJS 模块加载 ES6 模块
      • 不能使用require命令,而要使用import()函数。ES6 模块的所有输出接口,会成为输入对象的属性。

    4. 循环加载

      • CommonJS 模块加载原理
      • require命令第一次加载该脚本,就会执行整个脚本,然后在内存生成一个对象。
      • CommonJS模块无论加载多少次,都只会在第一次加载时运行一次,以后再加载,就返回第一次运行的结果,除非手动清除系统缓存。
      • 由于 CommonJS 模块遇到循环加载时,返回的是当前已经执行的部分的值,而不是代码全部执行后的值,两者可能会有差异。所以,输入变量的时候,必须非常小心。

      • ES6 模块的循环加载
      • 与CommonJS不同:变量不会被缓存,而是成为一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。
      • a 引用 b,b中引用a,执行到b中对a引用时会认为a已经存在,继续往下执行
    展开全文
  • 在线转换 Babel 提供一个REPL在线编译器,可以在线将 ES6 代码转为 ES5 代码。转换后的代码,可以直接作为 ES5 代码插入网页运行。
  • ES6_30分钟速读_新特性

    2018-05-17 12:12:00
    ECMAScript 2015 FeaturesES6 包含了以下这些新特性:Arrows 箭头函数Classes 类Enhanced Object Literals 增强的对象字面量Template Strings 模板字符串Destructuring 解构Default + Rest + Spread 默认参数+不定...
  • ES6是什么?

    2017-02-17 18:33:58
    ES6 就是ECMAScript 6是新版本JavaScript语言的标准。虽然目前已经更新到ES7,但是很多浏览器还不知处ES7语法,该标准仍在更新中,但目前部门网站都指出ES6的语法。目前ES6也是使用最多的最新的javaScript语言标准...
  • Es6(...)

    2019-04-28 11:29:40
    1  含义扩展运算符( spread )是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。console.log(...[1, 2, 3])// 1 2 3console.log(1, ...[2, 3, 4], 5)// 1 2 3 4 5[...
  • es6之扩展运算符 三个点(...)es6之扩展运算符 三个点(...)对象的扩展运算符数组的扩展运算符总结 es6之扩展运算符 三个点(…) 对象的扩展运算符 理解对象的扩展运算符其实很简单,只要记住一句话就可以: ...
  • 本套课程讲解了运行环境已支持的 ES6 特性,而忽略掉未支持的特性,随着 Node.js 、babel 和浏览器等 ES6 运行环境的逐步支持,本套视频课程的内容也会补充。这样授课的好处是可以让学员直接在开发中实际的应用 ES6 ...
  • 用babel将es6转换成es5

    2016-07-19 18:21:50
    ECMAScript 6(ES6)的发展速度非常之快,但现代浏览器对ES6新特性支持度不高,所以要想在浏览器中直接使用ES6的新特性就得借助别的工具来实现。Babel是一个广泛使用的转码器,babel可以将ES6代码完美地转换为ES5代码...
  • 转载自AlloyTeam:前端开发者不得不知的ES6十大特性 ES6(ECMAScript2015)的出现,无疑给前端开发人员带来了新的惊喜,它包含了一些很棒的新特性,可以更加方便的实现很多复杂的操作,提高开发人员的效率。 本文...
  • 由于广大用户使用的浏览器版本在发布的时候也许早于ES6的定稿和发布,而到了今天,我们在编程中如果使用了ES6的新特性,浏览器若没有更新版本,或者新版本中没有对ES6的特性进行兼容,那么浏览器肯定无法识别我们的...
  • ES6数组去重的三个简单办法 简单说一下利用ES6实现数组去重的三个办法。 第一种: 利用Map对象和数组的filter方法 贴上相关代码 打印后的结果 通过打印我们发现,确实实现了我们想要的效果。那么下面简单来解释一下...
  • 文章目录什么是ES6为什么要使用ES6 什么是ES6 ES的全称是 ECMAScript, 它是由ECMA国际标准化组织制定的一项脚本语言的标准化规范。 ES6 实际上是一个泛指,泛指 es2015 即后需的版本 为什么要使用ES6 每一次标准的...
  • 深入浅出ES6

    2020-07-15 23:34:04
    欢迎来到ES6深入浅出!JavaScript的新版本离我们越来越近,我们将通过每周一节的系列课程一起探索 ECMAScript 6新世界。ES6中包含了许多新的语言特性,它们将使JS变得更加强大,更富表现力。在接下 来的几周内,我们...
  • ES6中的模块即使一个包含JS代码的文件,在这个模块中所有的变量都是对其他模块不可见的,除非我们导出它。ES6的模块系统大致分为导出(export)和导入(import)两个模块。 1、模块导出(export) 你可以导出所有的...
1 2 3 4 5 ... 20
收藏数 153,044
精华内容 61,217
关键字:

es6