| 
 
| 一、ES6的概念 ES6是 新一代的JS语言标准,包含ES2015、ES2016、ES2017、ES2018等。ES2015特指在2015年发布的新一代JS语言标准,现阶段在绝大部分情景下,ES2015默认为ES6。ES2015可以理解为ES5和ES6的时间分界线。
 
 二、变量声明
 ES5里面var没有块级作用域,这样一来,会导致很多问题。例如for循环变量泄露,变量覆盖,变量提升等问题。
 ES6用let来声明变量,let声明的变量拥有自己的块级作用域。
 
 三、String字符串
 ES6新增了字符串模板语法,在拼接字符串时,用` 字符串 `的形式,同时能保留所有空格和换行。除此之外,还能在字符串中嵌入变量。`字符串${变量名}字符串`。取代了ES5中用加号+进行字符串进行相加。
 语法改进:
 
 let age = 11;
 let str = `xiao ming is ${age} years old`;
 console.log(str);     //xiao ming is 11 years old;
 
 新的方法:
 ES6在原型上新增了Includes()方法,取代传统indexOf()查找字符串的方法。
 includes():找得到返回true,找不到返回false。
 indexOf():找得到返回0,找不到返回1。
 此外,还新增了startsWith(),endsWith(),padStart(),padEnd(),repeat()等方法,可用于查找,补全字符串。
 
 let str = `xiao ming`;
 console.log(str.includes(`xiao`));     //true
 
 四、Array数组
 数组解构赋值
 声明较多变量时,不用写很多的let变量声明关键字。
 
 let [a,b,c,d] = [1,2,3,4];   //a=1,b=2,c=3,d=4
 
 扩展运算符
 ES6新增的扩展运算符(…),可以实现数组和松散序列的相互转化,可以轻松取代arguments对象和apply方法,轻松获取未知参数个数情况下的参数集合。
 ES5中的arguments并不是真正的数组,而是一个类数组对象,但是运用扩展运算符的逆运算可以返回一个真正的数组。
 
 //ES5
 function fun(){
 console.log(arguments);
 }
 fun();      //{ '0': [ 1, 2, 3, 4 ] }
 
 //ES6
 function fun(){
 console.log(...arguments);
 }
 fun();     //[ 1, 2, 3, 4 ]
 
 还可以实现数组的复制和解构赋值。
 
 let arr = [2,3,4];
 let b = [...a];
 console.log(b);      //[2,3,4]
 
 五、Number类型
 ES6在原型上新增了isFinite(),isNaN()方法。ES5中全局方法isFinite(),isNaN(),都会将非数值类型的参数转化为Number类型再做判断。
 ES中isNaN()的不足,"NaN"是一个字符串,但是返回的是true,意味着是NaN。
 
 isNaN("NaN") === true;        //true
 
 Number.isNaN("NaN") === true;   //false
 
 ES6在Math对象上新增了Math.cbrt(),trunc(),hypot()等等较多的科学计数法运算方法,可以更全面的进行立方根、求和立方根等等科学计算。
 
 六、Object类型
 a.对象属性变量式声明。
 ES6可以直接以变量形式声明对象属性或者方法。比传统的键值对形式声明更加简洁,更加方便,语义更加清晰。
 
 let [apple,orange] = ["little apple","big orange"];
 console.log(apple);           //"little apple"
 let fruit = {apple,orange};
 console.log(fruit);           //{ apple: 'little apple', orange: 'big orange' }
 
 b.尤其在对象解构赋值或者模块输出变量时,更能体现好处:
 
 let Object = {apple:'little apple", orange:"big orange", banana::"middle banana"};
 let {apple,orange,banana} = Object;
 console.log(apple);             //"little apple"
 let fruit =  {apple,orange,banana};
 console.log(fruit);             //{apple:'little apple", orange:"big orange", banana::"middle banana"};
 
 可以看到属性变量式声明属性看起来更加简洁明了。方法也可以简洁写法:
 
 let es5Obj = {
 method: function() {}
 }
 let es6Obj = {
 method(){}
 }
 
 c.对象的扩展运算符(…)
 ES6对象的扩展运算符和数组扩展运算符用法本质上差别不大,毕竟数组也是特殊的对象。对象扩展运算符的用处在于可以轻松取出一个目标对象内部全部或者部分的可遍历属性,实现对象的合并和分解。
 
 let {apple, ...otherFruit} = {apple:'little apple", orange:"big orange", banana::"middle banana"};
 console.log(otherFruit);                   //{orange:"big orange", banana::"middle banana"};
 let moreFruits = {peak: "peak"};
 let allFruit = {apple, ...otherFruit,...moreFruits};
 
 d.ES6在Object原型上新增is()方法,比较两个目标对象,用来完善“===”方法。
 
 NaN === NaN      //false
 Object.is(NaN,NaN);     //true
 
 e.ES6在Object原型上新增了assign()方法,用于对象新增属性或者多个对象合并。
 
 const target = {a:1};
 const source1 = {b:2};
 const source2 = {c:3};
 Object.assign(target,source1,source2);
 console.log(target);          //{a:1,b:2,c:3}
 
 assign合并的对象target只能合并source1、source2中的自身属性,并不会合并source1、source2中的继承属性,也不会合并不可枚举属性,且无法正确复制get和set属性(会直接执行get/set函数,取return值)。
 
 f.ES6在原型上新增了getOwnPropertyDescriptors()方法,增强了ES5的相同方法,可以获取指定对象所有自身属性的描述对象。结合defineProperties()方法,可以完美复制对象,包括复制get和set属性。
 
 g.ES6在原型对象上新增了getPrototypeOf()和setPrototyprOf()方法,用来获取或设置当前对象的原型prototype对象。
 
 h.ES6在Object原型上新增了Object.keys(),Object.values(),Object.entires()方法,用来获取对象的所有键、所有值和所有键值对数组。
 
 七、Function函数类型
 a.箭头函数
 ES6箭头函数内的this指向的是函数定义时所在的对象,而不是函数执行时所在的对象。箭头函数背部没有自己的this,this总是指向上一层的this,层层递上,直到找到有自己的this函数为止。
 ES5函数里的this总是指向函数执行时所在的对象,尤其在非严格模式下,this有时候会指向全局对象。
 
 b.箭头函数不能用构造函数,因为它没有自己的this,无法实例化。
 
 c.箭头函数没有自己的this,所以函数内也不存在arguments对象。这里就可以ES6的扩展运算符来代替。
 
 d.函数的默认赋值
 ES5,函数的形参是无法给默认值得,只能在函数内部通过变通方法实现。
 ES6,函数可以默认赋值。
 
 function es6Fun(a = 1,b = 2,c = 3){
 console.log(a,b,c);
 }
 es6Fun();         //1,2,3
 function es6Fun1(a,b = "default"){
 console.log(a,b);
 }
 es6Fun1("666");   //"666","default"
 es6Fun1(1,2);     //1,2
 
 e.ES6新增了双冒号运算符,用来取代以往的bind,call,和apply。
 
 //ES5
 bar.bind(foo);
 bar.apply(foo,arguments);
 //ES6
 foo::bar;
 foo::bar(...arguements);
 
 八、Symbol类型
 Symbol是ES6引入的第七种原始数据类型,所有Symbol()生成的值都是独一无二的,可以从根本上解决对象属性太多导致属性名冲突覆盖的问题。
 Symbol值通过Symbol函数生成。
 对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种是新增的symbol类型,凡是属性名属于symbol类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。
 
 let s  = Symbol();
 typeof s           //'symbol'
 
 Symbol函数前不能使用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。
 Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。
 
 let s1 = Symbol('foo');
 let s2 = Symbol('bar');
 
 s1 // Symbol(foo)
 s2 // Symbol(bar)
 
 s1.toString() // "Symbol(foo)"
 s2.toString() // "Symbol(bar)"
 
 s1和s2是两个 Symbol 值。如果不加参数,它们在控制台的输出都是Symbol(),不利于区分。有了参数以后,就等于为它们加上了描述,输出的时候就能够分清,到底是哪一个值。
 
 // 没有参数的情况
 let s1 = Symbol();
 let s2 = Symbol();
 
 s1 === s2 // false
 
 // 有参数的情况
 let s1 = Symbol('foo');
 let s2 = Symbol('foo');
 
 s1 === s2 // false
 
 Symbol 值不能与其他类型的值进行运算,会报错。Symbol 值可以显式转为字符串。另外,Symbol 值也可以转为布尔值,但是不能转为数值。
 
 作为属性名的symbol
 由于每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
 
 let mySymbol = Symbol();
 
 // 第一种写法
 let a = {};
 a[mySymbol] = 'Hello!';
 
 // 第二种写法
 let a = {
 [mySymbol]: 'Hello!'
 };
 
 // 第三种写法
 let a = {};
 Object.defineProperty(a, mySymbol, { value: 'Hello!' });
 
 // 以上写法都得到同样结果
 a[mySymbol] // "Hello!"
 
 
 Symbol 值作为对象属性名时,不能用点运算符。
 在对象的内部,使用 Symbol 值定义属性时,Symbol 值必须放在方括号之中。
 
 对象中Symbol()属性不能被for…in遍历,但是也不是私有属性。
 
 九、Set,类似Array的新的数据结构、
 Set是ES6引入的一种类似Array的新的数据结构,Set实例的成员类似于数组item的成员。
 区别:Set实例的成员都是唯一的,不重复的。这个特性可以轻松实现数组去重。
 简单来说,本质是数组,最核心就是数组去重。
 
 let array = [1,2,3,2,3];
 let set = new Set(array);
 console.log(set);           //[1,2,3]
 
 let o1 = {age:11}
 let o2 = {age:12}
 let set = new Set([o1,o2]);
 console.log(set);           //结果里面有两个对象吧,因为对象的内存地址不一样
 let o2 = o1;
 let set = new Set([o1,o2]);
 console.log(set);           //结果里面有一个对象,对象的内存地址一样
 
 当然,Set也有自己的属性和方法。
 
 属性
 .size 返回set集合的大小
 方法
 .add()<相当于数组的push()> .delete(value) .has(value) .clear()清除数组
 
 Set也可以转换为数组。
 
 
 let array = Array.from(set);
 
 
 遍历Set和遍历Array一样。
 
 let set = new Set([1,2,3,3,4]);
 for(let v of set){
 console.log(v);
 }
 
 十、Map,类似Object的新的数据结构
 Map是ES6引入的一种类似Object的新的数据结构,Map可以理解为是Object的超集,打破了以传统键值对形式定义对象,对象的key不再局限于字符串,也可以是Object。
 
 let map1 = new Map()
 let obj = {name:'tiger'}
 map1.set(obj,'hello0')     //设置键值对
 map1.set(1,'hello1')
 map1.set(2,'hello2')
 alert(map1.get(obj));     //取值get
 //遍历键值对
 for(let item of map1){
 console.log(item);
 }
 //遍历键
 for(let item of map1.keys()){
 console.log(item);
 }
 //遍历值
 for(let item of map1.value()){
 console.log(item);
 }
 
 
 十一、Promise对象
 Promise是ES6引入的一个对象,主要作用是来解决JS异步机制里,回调机制产生的“回调地狱”。使得异步回调可以写的更加优雅,可读性更高,而且可以链式调用。
 
 function ajaxPromise() {
 return new Promise(function(reslove,reject) {
 console.log("正在执行...");
 if(true) {
 reslove("success")
 }else{
 reject("fail")
 }
 })
 }
 
 ajaxPromise().then(value => {
 console.log(value);
 }).catch(err => {
 console.log(err);
 })
 
 
 更详细的讲解可参考:https://www.jianshu.com/p/c98eb98bd00c
 
 十二、Generator函数
 执行Generator函数会返回一个遍历器对象,每一次Generator函数里面的yield都相当一次遍历器对象的next()方法,并且可以通过next(value)方法传入自定义的value,来改变Generator函数的行为。
 定义函数的时候需要加上星号*,例:function * test(){}
 函数体里面包含yield(暂停执行的标志),代码运行到yield的时候便会暂停执行,然后通过.next(),让代码继续执行,直到下一个yield或者函数体结束符。
 
 function * test(){
 let n = 0;
 n++;
 console.log(n);
 yield n;//产出
 n++;
 console.log(n);
 yield n;//产出
 return n;
 }
 let test = test();
 test.next();
 text.next();
 
 
 十三、async函数
 async函数可以理解为内置自动执行器的Generator函数语法糖,它配合ES6的Promise近乎完美的实现了异步编程解决方案。
 
 将generator的*替换成async,yield替换成await;
 await返回的是promise对象,或者是promise对象的resolve方法;
 async函数里面的return返回的也是promise对象,所以执行结果也是用.then接收返回值;
 await在async函数内部,但不能嵌套在其它函数的内部;
 
 案例情景:
 如果async函数内部有多个await操作时,如果其中某个await出错,代码就无法继续执行,相当于break操作;
 try{…}catch(e){…}finally{…}
 将所有await操作放在try内部,结果还是当其中某个await出错的时候,try内的代码还是无法继续执行,但是try外的代码会继续执行;
 所以当有多个请求的时候,可以利用for循环,循环多个await操作,这样即使其中某个await操作出错,不会影响其他代码的执行;
 
 async function fun(){
 }
 
 十四、Class、extends
 ES6的class可以看作只是一个ES5生成实例对象的构造函数的语法糖。它参考了Java语言,定义了一个类的概念,让对象原型写法更加清晰,对象实例化更像是一种面向对象编程。
 Class类可以通过extends实现继承。它和ES5构造函数的不同点:
 a.类的内部定义的所有方法,都是不可枚举的。
 b.ES6的class类必须用new命令来操作,而ES5的构造函数不用new也可以执行。
 c.ES6的class类不存在变量提升,必须先定义class之后才能实例化,不像ES5中可以将构造函数写在实例化之后。
 d.ES5的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面。ES6的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。
 e.在class里面定义的方法都是原型链上,都是属于原型属性.
 f.实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)
 
 //ES5
 function ES5Fun(a,b) {
 this.a = a;
 this.b = b;
 }
 ES5Fun.prototype.toString = function() {
 return `(${this.a},${this.b})`;
 }
 
 let e = new ES5Fun(1,2);
 e.toString();                    //(1,2);
 Object.keys(ES5Fun.prototype);   //['toString']
 
 //ES6
 class ES6Fun{
 constructor(a,b) {
 this.a = a;
 this.b = b;
 toString() {
 return `(${this.a},${this.b})`;
 }
 }
 Object.keys(ES6Fun.prototype);       //[],class内定义的方法都是不可枚举属性
 Object.getOwnPropertyNames(ES6Fun.prototype)  //["constructor", "toString"],返回所有方法属性
 Object.hasOwnProperty("方法名“);   //实例属性返回true,继承属性返回false.class内都返回false,因为都是定义在原型上,而非实例上
 
 十五、模块化开发
 为什么要模块化开发呢?
 有两个作用,其一,解决模块中函数名重复的问题;其二,解决模块之间的依赖关系。
 可以取代commonJS和AMD规范,实现浏览器和服务器的统一。
 自动开启严格模式,在每个模块头部自动添加“use strict“。
 export用于导出模块,import用于导入模块。
 例子:假设在同目录下有a.js和b.js这两个js文件
 
 
 
 | 
 |