目录
- 什么是ES6?
- let和const
- 字符串模板
- 解构赋值
- 复制数组
- Map
- for-of循环
- 箭头函数
- 对象的简洁语法
- class和extends
- 模块化-export,import
- Promise
- Generator、yield
1.什么是ES6?
ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准。因为当前版本的ES6是在2015年发布的,所以又称ECMAScript 2015(简称ES2015)。虽然浏览器在不断更新,但并不是所有用户的电脑浏览器都支持ES6,所以在使用的过程中建议还是转成es5,保证代码的可执行性。至于转换的方式大家可以用Babel或者Traceur转码器。
2. let和const
在ES6以前,Javascript并不同有块级作用域的概念,有的是函数作用域,而let的出现就是为了打破局面,有点向后台语言发展的趋势。const是代表常量,必须在定义的时候初始化,不可改变。下面我举例子说明。
window.onload = function(){ var aInput = document.getElementsByTagName("input"); // 传统解决办法 for(var i=0;i<aInput.length;i++){ (function(i){ // 函数闭包自执行来解决i索引的问题 aInput[i].onclick = function(){ alert(i); }; })(i); } // let变量的出现相当于给你加了一个封闭空间来极度简化了i值索引的问题 // let大家可以看成是匿名函数立即调用(IIFE) for(let i=0;i<aInput.length;i++){ aInput[i].onclick = function(){ alert(i); }; } }; //对于let和const来说,变量不能重新声明,所以一旦赋值的变量以前声明过,就会报错,而以前var的时候并不会报错,只是覆盖定义 let a = 12; let a = 13; //Identifier 'a' has already been declared //const必须在初始化的时候同时赋初值,且不能更改 const b; //Uncaught SyntaxError: Missing initializer in const declaration const b = 14; b = 15; //Uncaught TypeError: Assignment to constant variable.
3. 字符串模板
//传统字符串拼接 var s1 = '快乐童年放飞希望^_^'; var s2 = '国家主席^_^'; var str = '2013年5月29日,中共中央总书记、'+s2+'、中央军委主席习近平在'+ '北京市少年宫参加“'+s1+'”主题队日活动。这是习近平在和孩子们谈对环保的认识和'+'理解。新华社记者李学仁摄'; document.write(str); // 字符模板的写法 var s1 = '快乐童年放飞希望^_^'; var s2 = '国家主席^_^'; var str = `2013年5月29日,中共中央总书记、${s2}、中央军委主席习近平在北京市少年宫参加“${s1}”主题队日活动。这是习近平在和孩子们谈对环保的认识和理解。 新华社记者李学仁摄`; document.write(str);
4.解构赋值
// 以前我们给变量赋值,只能直接指定值 var a = 1; var b = 2; var c = 3; console.log(a,b,c); // 1 2 3 // 现在用解构赋值的写法就变得简单了,只要模式匹配上了就行了,如下 // 注意数组是有顺序的 var [a,b,c] = [11,22,33]; console.log(a,b,c); // 11 22 33 var [b,a,c] = [11,22,33]; console.log(a,b,c); // 22 11 33 // 当然解构赋值还有嵌套比较复杂的写法,如下 let [foo,[[bar],[baz]]] = [111,[[222],[333]]]; console.log(foo,bar,baz); // 111 222 333 let [head,...foot] = [1,2,3,4]; console.log(head,foot); // 1 [2,3,4] // 如果解构不成功,变量的值就等于undefined,如下 var [bar3,foo3] = [1000]; console.log(bar3,foo3); // 1000 undefined // 另一种情况是不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功 let [x,y] = [10000,20000,30000]; console.log(x,y); // 10000 20000 // 默认值可以引用解构赋值的其他变量,但该变量必须已经声明 let [a=1,b=a] = [2,3]; console.log(a,b); // 2 3 // 对象的解构也可以指定默认值 var {x,y=5} = {x:1}; console.log(x,y); // 1 5 //对象的解构赋值解构不仅可以用于数组,还可以用于对象(json) //对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定; //而对象的属性没有次序,变量必须与属性同名,才能取到正确的值 var {a,b} = {a:'apple',b:'banana'}; console.log(a,b); // apple banana var {b,a} = {a:'apple',b:'banana'}; console.log(a,b); // apple banana // 如果变量名与属性名不一致,必须写成下面这样 let obj = {first:'hello',last:'world'}; // first ---> f,那么此时f就是first,而不是undefined了,有点类似别名的概念 let {first:f,last} = obj; console.log(f,last); // hello world //1.也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。 真正被赋值的是后者,而不是前者 //2.v是匹配的模式,n才是变量。真正被赋值的是变量n,而不是模式v。 //注意,采用这种写法时,变量的声明和赋值是一体的 // v ---> n,那么此时n就是vue,而不是undefined了 var {v:n} = {v:'vue',r:'react'}; console.log(n); // vue console.log(v); // Uncaught ReferenceError: v is not defined console.log(r); // Uncaught ReferenceError: r is not defined
5.复制数组
// 数组的浅拷贝,引用之间的拷贝,没有实现数组的真正复制 var arr1 = [1, 2, 3]; var arr2 = arr1; arr2.push(4); console.log(arr1, arr2); // 复制数组深拷贝,传统做法 var arr1 = [1,2,3]; var arr2 = []; //通过for循环遍历之后将arr1数组的每一项赋值给arr2数组的每一项, 就实现了数组的深拷贝,这时候我再去操作arr2的数组的时候,arr1就不会受影响了 for(var i=0;i<arr1.length;i++){ arr2[i] = arr1[i]; } // 数组尾部添加 arr2.push(4); console.log(arr1,arr2); // ES6实现的数组的深拷贝方法1 var arr1 = [1,2,3]; var arr2 = Array.from(arr1); // 数组尾部添加 arr2.push(100); console.log(arr1,arr2); // ES6实现的数组的深拷贝方法2 var arr1 = [1,2,3]; // 超引用拷贝数组 var arr2 = [...arr1]; // 数组尾部添加 arr2.push(1000); console.log(arr1,arr2); function show(...args){ // 此时这个形势参数就是一个数组,我们可以直接push东西进来,如下 args.push(5); console.log(args); } // 调用 show(1,2,3,4); // 1,2,3,4,5
6. Map
var map = new Map(); // 设置 // map.set(name,value); map.set('a','apple'); map.set('b','banana'); // 获取 // map.get(name); console.log(map.get('a') + ' ' + map.get('b')); // 删除之前map对象 console.log(map); // 删除 // map.delete(name); map.delete('a'); // 删除之后map对象 console.log(map); // 注意for..in是不能循环map对象的,不报错也无任何反应,稍微注意下 for(var name in map){ console.log(name); } // 实体 map=map.entries() for(var name of map){ //循环出来的结果就是:a,apple b,banana 循环key,value console.log(name); } //循环出来的结果就是: a,apple b,banana 循环key,value for(var [key,value] of map.entries()){ console.log(key,value); } //只循环key for(var key of map.keys()){ console.log(key); } //只循环value for(var val of map.values()){ console.log(val); }
7. for-of循环
//for of一个arr对象 var arr = ['红楼梦','西游记','三国演义','水浒传','金瓶梅']; //只循环key for(var key of arr.keys()){ console.log(key); } //只循环value,注意数组是没有.values() for(var value of arr){ console.log(value); } //循环key,value for(var [key,value] of arr.entries()){ console.log(key,value); } //for in循环与for of循环的区别 var arr = ['apple','banana','orange','pear']; for(var i in arr){ // i打印出来的就是arr数组对应的索引 // 0 1 2 3 console.log(i); } for(var i of arr){ // i值打印出来的就是我们想要的数组具体的值 // apple banana orange pear console.log(i); } //for of不能循环json var json = {'a':'apple','b':'banana','c':'orange','d':'pear'}; for(var name in json){ // a b c d console.log(name); // apple console.log(json.a); // pear console.log(json['d']); } // 注意for..of可以循环arr,但是不可以循环json,会报错,特别注意下 for(var name of json){ Uncaught TypeError: undefined is not a function console.log(json); } 8. 箭头函数 //箭头函数写法 function(){} 变为 ()=>{} window.onload = () => { var oBox = document.getElementById("box"); oBox.onclick = () => { oBox.style.backgroundColor = '#ff0000'; }; }; //注意this指向会有问题 var json = { a:1, b:2, showName:() => { return this.a; } }; // 因为使用了箭头函数this指向了object window 所以result:undefined console.log(json.showName()); //如果使用了箭头函数的写法,那么注意arguments将不能继续使用了 var show = () => { console.log(arguments); }; // Uncaught ReferenceError: arguments is not defined show(1,2,3);
8. 箭头函数
//箭头函数写法 function(){} 变为 ()=>{} window.onload = () => { var oBox = document.getElementById("box"); oBox.onclick = () => { oBox.style.backgroundColor = '#ff0000'; }; }; //注意this指向会有问题 var json = { a:1, b:2, showName:() => { return this.a; } }; // 因为使用了箭头函数this指向了object window 所以result:undefined console.log(json.showName()); //如果使用了箭头函数的写法,那么注意arguments将不能继续使用了 var show = () => { console.log(arguments); }; // Uncaught ReferenceError: arguments is not defined show(1,2,3);
9. 对象的简洁语法
//传统对象_单体模式写法 key-value模式 var person = { name:'jam', age:28, showName:function(){ return this.name; }, showAge:function(){ return this.age; } }; // 调用 console.log(person.showName()); // jam console.log(person.showAge()); // 28 //ES6_单体模式写法 不需要写key var name = 'xiaokai'; var age = 2; var person = { name, age, showName(){ return this.name; }, showAge(){ return this.age; } }; // 调用 console.log(person.showName()); // xiaokai console.log(person.showAge()); // 2
10. class和extends
//传统面向对象写法 function Person(name,age){ // 类、构造函数 this.name = name; this.age = age; } Person.prototype.showName = function(){ return this.name; }; Person.prototype.showAge = function(){ return this.age; }; var p1 = new Person('allen',28); var p2 = new Person('xiaoxiaoyou',101); console.log(p1.showName()); // allen console.log(p2.showAge()); // 101 console.log(p1.showName == p2.showName); // true console.log(p1.constructor == Person); // true //ES6面向对象写法 class Person{ // 构造器 constructor(name,age){ this.name = name; this.age = age; } showName(){ return this.name; } showAge(){ return this.age; } } var p1 = new Person('aaa',18); var p2 = new Person('bbb',20); console.log(p1.name); // aaa console.log(p1.showName()); // aaa console.log(p2.showAge()); // 20 console.log(p1.showAge == p2.showAge); // true console.log(p1.constructor == Person); // true //面向对象class给默认值 class Person{ // 构造器 constructor(name='default',age=0){ this.name = name; this.age = age; } showName(){ return this.name; } showAge(){ return this.age; } } var p1 = new Person(); console.log(p1.name); // 构造器里面给的默认值 default console.log(p1.age); // 构造器里面给的默认值 0 //传统写法原型继承 function Person(name,age){ // 类、构造函数 this.name = name; this.age = age; } Person.prototype.showName = function(){ return this.name; }; Person.prototype.showAge = function(){ return this.age; }; // 工人类 function Worker(name,age){ // 属性继承过来 Person.apply(this,arguments); } // 原型继承 Worker.prototype = new Person(); var p1 = new Person('allen',28); var w1 = new Person('worker',1000); console.log(w1.showName()); // 确实继承过来了 result:worker //ES6中面向对象实现类继承 class Person{ // 构造器 constructor(name,age){ this.name = name; this.age = age; } showName(){ return this.name; } showAge(){ return this.age; } } class Worker extends Person{ constructor(name,age,job='拖地的'){ // 继承超父类的属性 super(name,age); this.job = job; } showJob(){ return this.job; } } var p1 = new Person('aaa',18); var w1 = new Person('www',36); var w2 = new Worker('wwwwwwww',90); console.log(w1.showName()); // www console.log(w2.showJob()); // 默认给的值 ‘拖地的’
11. 模块化-export和import
下边的演示文件mod代表是导出模块js, index代表的是引入模块的js ,mod文件跟index文件一一对应关系 。
//mod.js // 第一种模块导出的书写方式(一个个的导出) // 导出普通值 export let a = 12; export let b = 5; // 导出json export let json = { a, b }; // 导出函数 export let show = function(){ return 'welcome'; }; // 导出类 export class Person{ constructor(){ this.name = 'jam'; } showName(){ return this.name; } } //index.js //导出模块如果用default了,引入的时候直接用,若没有用default,引入的时候可以用{}的形式 // 导入模块的方式 import { a, b, json, show, Person } from './mod.js'; console.log(a); // 12 console.log(b); // 5 console.log(json.a); // 12 console.log(json.b); // 5 console.log(show()); // welcome console.log(new Person().showName()); // jam //mod1.js // 第二种模块导出的书写方式 let a = 12; let b = 5; let c = 10; export { a, b, c as cc // as是别名,使用的时候只能用别名,特别注意下 }; //index1.js // 导入模块的方式 import { a, b, cc // cc是导出的,as别名 } from './mod1.js'; console.log(a); // 12 console.log(b); // 5 console.log(cc); // 10 //mod2.js // 第三种模块导出的书写方式 ---> default // default方式的优点,import无需知道变量名,就可以直接使用,如下 // 每个模块只允许一个默认出口 var name = 'jam'; var age = '28'; export default { name, age, default(){ console.log('welcome to es6 module of default...'); }, getName(){ return 'bb'; }, getAge(){ return 2; } }; //index2.js // 导入模块的方式 import mainAttr from './mod2.js'; var str = ' '; // 直接调用 console.log(`我的英文名是:${mainAttr.name}我的年龄是${mainAttr.age}`); mainAttr.default(); // welcome to es6 module of default... console.log(mainAttr.getName()); // bb console.log(mainAttr.getAge()); // 2 //mod3.js var name = 'jam'; var age = '28'; export function getName(){ return name; }; export function getAge(){ return age; }; //index3.js // 导入模块的方式 import * as fn from './mod3.js'; // 直接调用 console.log(fn.getName()); // jam
12. Promise
//Promise对象 ---> 用来传递异步操作过来的数据的 //Pending(等待、处理中) ---> Resolve(完成,fullFilled) ---> Reject(拒绝,失败) var p1 = new Promise(function(resolve,reject){ resolve(1); // 成功了 // reject(2); // 失败了 }); // 接收成功和失败的数据,通过then来传递 // then也是返回一个promise对象,会继续往下传递数据,传递给下一个then p1.then(function(value){ // resolve console.log(value); return value + 1; // 1 alert(`成功了:${value}`); },function(value){ // reject alert(`失败了:${value}`); }).then(function(value){ console.log(value); // 2 }); //catch捕获异常错误 var p1 = new Promise(function(resolve,reject){ resolve('成功了'); }); p1.then(function(value){ console.log(value); // throw是用来抛错误的 throw '发生了点小意外'; }).catch(function(e){ // catch用来捕获这个错误的 ---> 追踪 console.log(e); }); //all ---> 全部,用于将多个promise对象,组合,包装成 //Promise.all([p1,p2,p3,...]); 所有的promise对象,都正确,才走成功 //否则,只要有一个错误,就走失败 var p1 = Promise.resolve(1); var p2 = Promise.reject(0); Promise.all([true,p1,p2]).then(function(obj){ console.log(`成功了:${obj}`); },function(obj){ console.log(`失败了:${obj}`); }); // race ---> 返回的也是一个promise对象 //最先执行的的promise结果,哪个最快我用哪个 var p1 = new Promise(function(resolve,reject){ setTimeout(resolve,50,'one'); }); var p2 = new Promise(function(resolve,reject){ setTimeout(resolve,100,'two'); }); Promise.race([p1,p2]).then(function(val){ console.log(val); }); //resolve ---> 生成一个成功的promise对象 //语法规则:Promise.resolve(val); // 普通值 // Promise.resolve(arr); // 数组之类 //Promise.resolve(promise); // 传递另一个promise对象 //传递普通值 Promise.resolve('success').then(function(val){ // 注意resolve,走得是这里 console.log(val); // success },function(err){ console.log("err:"+ err); }); //传递数组 Promise.resolve([1,2,3]).then(function(val){ // 注意resolve,走得是这里 console.log(val); // [1,2,3] },function(err){ console.log(err); }); //传递一个promise对象 var p1 = Promise.resolve(520); var p2 = Promise.resolve(p1); p2.then(function(val){ //从p1那边传递过来的 console.log(val); // 520 });
13. Generator、yield
//Generator ---> 生成器就是一个函数 //特点: //1.函数名前面带一个*,和普通函数做区分 //2.内部使用yield语句 //调用方式,如下var res = show(); //value指的是generator函数内容yield定义的值,done:false表示还没遍历完 //直接找到返回值return了,那么此时done才会为true //console.log(res.next());{value:'值1',done:false} function* show(){ yield 'Hello'; yield 'World'; yield 'ES6'; return 'xx'; } var res = show(); console.log(res.next()); // {value: "Hello", done: false} console.log(res.next()); // {value: "World", done: false} console.log(res.next()); // {value: "ES6", done: false} console.log(res.next()); // {value: "allen", done: true} // 已经找到return返回值了,继续下去就没有意义了 // console.log(res.next()); // {value: "undefined", done: true} //yield本身没有返回值,或者可以说每次给你返回的是undefined function* show(){ var a = yield 'Hello'; return a; } var res = show(); console.log(res.next()); // {value: "Hello", done: false} console.log(res.next()); // {value: "undefined", done: true} //next方法是可以带参数的,死循环的generator函数 function* fn(){ for(var i=0;true;i++){ // 如果里面传了一个值,那么它会把这个参数赋给最近的一个yield var a = yield i; if(a) i = -1; } } var d = fn(); console.log(d.next()); // {value: 0, done: false} console.log(d.next()); // {value: 1, done: false} console.log(d.next()); // {value: 2, done: false} // 如果里面传了一个值,那么它会把这个参数赋最近的一个yield console.log(d.next(true)); // {value: 0, done: false} console.log(d.next()); // {value: 1, done: false} console.log(d.next()); // {value: 2, done: false} console.log(d.next()); // {value: 3, done: false} // for..0f循环generator函数 function* fn(){ yield 1; yield 2; yield 3; yield 4; yield 5; return 6; } //for..0f循环generator函数,可以取值 for(let val of fn()){ document.write(val); // 12345 } // 对象里使用generator函数的特殊写法,注意下 var json = { *show(){ yield 'a'; yield 'b'; return 'c'; } }; var res = json.show(); console.log(res.next()); // {value: "a", done: false} console.log(res.next()); // {value: "b", done: false} console.log(res.next()); // {value: "c", done: true}