es6新增新特性简要总结
es6简介
es6是在2015年6月正式颁布的新标准,es6基本上实现了所有ECMAScript 规范,以后每年的6月都会发布新版本,但改动不大。
let 变量
使用let 关键字来申明的变量拥有以下特点:
作用域不同: let定义的变量是块级作用域 ;var定义的变量是函数作用域[局部的], 但在if、for等定义的变量是全局的。
同一个变量声明的次数:在同一个作用域中同一个变量let只能声明一次,而var可以多次声明;因此,let声明的变量可以避免全局变量被污染。
是否有变量预解析:var声明的变量存在变量提升,而let则没有变量提升,let声明的变量要先定义后使用。
let有暂时性死区:let声明的变量要先定义后使用。(// Cannot access 'i' before initialization)
示例: 即使上一个作用域有此变量,但是当前作用也申明了,那么变量也必须先申明再使用;不然还是报错,而且不会向上查找;
变量的绑定归属:let声明的变量不会绑定到顶层对象(顶层对象可以理解为最大的全局变量,即window对象)上。
const 常量
const是用来定义常量的。 常量就是 在整个程序运行期间其值是固定不变的。
const 特点 :
常量也是块级作用域、不存在声明提升、同一个常量只能声明一次;申明时候需要赋值初始值;
常量一旦声明其值不能改变,但要注意:如果常量的值为数组、对象时其值是可以被改变的;
解构赋值
结构赋值:在es6中将数组、对象中的数据按照一定模式提取出来并赋值给变量,这一过程称为解构赋值。
1. 数组解构赋值:四种方式
数组完全解构赋值;数组完全解构赋值 两边的值一一对应;
- let arr = [1, 3, 5, 9];
- let [x, y, m, n] = arr;
- console.log(x, y, m, n); //1, 3, 5, 9
数组不完全解构赋值; 如果参数超过数组的值 那么其变量为undefined;
- let arr = [1, 3, 5, 9];
- let [, o, , p, z] = [2, 4, 6, 7];
- console.log(o, p, z); //4 7 undefined
数组解构赋值默认值:要注意默认值在'解构失败'时才生效 ;
- let arr = [1, 3, 5, 9];
- let [l, o, p = 66, f = 88] = [1, 3, 5];
- console.log(l, o, p, f); //1 3 5 88
数组嵌套解构赋值: 变量与值的格式一一对应 一个' , '表示一个变量;
- let arr3 = [2, 3, [5, 6], 10];
- let [, p, [, f], l] = arr3;
- console.log(p, f, l); //3 6 10
2. 对象解构赋值:四种方式,和数组基本一致;
对象完全解构赋值; 键值一一对应,没有顺序要求;
- let obj = {
- 'name': '张三',
- 'age': 13
- }
- // 完整写法:
- let {
- 'name': x,
- 'age': y
- } = obj;
- console.log(x,y); // 张三 13
- // 简写1 :
- let {
- 'name': name,
- 'age': age
- } = obj;
- console.log(name,age); // 张三 13
- // 简写2 :
- let {
- name,
- age
- } = obj;
- console.log(name,age); // 张三 13
对象不完全解构赋值;
- let {
- y
- } = {
- "x": 11,
- "y": 2
- };
- console.log(y); // 2
对象解构赋值默认值:要注意默认值在'解构失败'时才生效;
- let {
- m,
- n = 9,
- z = 100
- } = {
- "m": 1,
- "n": 22
- };
- console.log(m, n, z); // 1 22 100
对象嵌套解构赋值;
对象解构赋值的应用:
数组解构赋值应用:交换数据;
- let obj = {
- 'ok': [1, 3, {
- "x": 11,
- "o": 9
- }, 33]
- };
- let {
- 'ok': [, n, {
- o
- }, z]
- } = obj;
- console.log(n, o, z); // 3 9 33
注意 : 需要是let申明的变量 用var 申明的会报错 就不能这样交换;
let x = 11,y = 3;//y=11; x=3;[y, x] = [x, y];console.log(x, y);//3 11
对象解构赋值应用:ajax方法;
/对象解构赋值应用:ajax方法//jquery封装的ajax方法://$.ajax()、$.get()、$.post() $.load()// $.get('地址'[,{要发送的参数}],回调方法[,'期望服务器端返回的数据类型']);//比如说返回结果d为:d = {"code":200,"msg":"成功","data":[]}$.get('http://www.baidu.com/api/demo.php', //function(d) {function ({code,msg,data}) {// let code = d.code;// let msg = d.msg;// let data = d.data;}, 'json');
字符串的解构赋值;
let str = 'hello';let [a,b,c,d,e] = str;console.log(a,b,c,d,e);//h e l l o
对象简写形式:就是在对象中可以写变量和函数,来作为对象的属性和方法。
// 对象简写形式:属性名简写、方法名简写 //属性名简写:当属性名与属性值同名时可以简写let usr = 'Tom';let email = 'tom@163.com';// let obj = {// "usr": usr,// "email": email// };// let obj = {// usr,// email,// "fn": function() {// console.log('fn!');// }// };//方法简写:let obj = {usr,email,fn() {console.log('fn...');}};obj.fn();console.log(obj, 888);
对象属性名表达式: 对象的属性名可以写表达式,而在es5中对象的属性名只能是字符串。
let y = 2;let x = 'age';let obj = {["demo" + y]: 33,[x]: 19}console.log(obj);//{demo2: 33, age: 19}console.log(obj["demo" + y]);//33console.log(obj[x]); //19
模板字符串
用途:用来简化字符串的拼接;
语法:`` [反引号];
模板字符串的特点:
在模板字符串中可以引用变量、函数调用、运算、换行。
let usr = "李四";let age = 21;// let str = "<a href=''>姓名:</a>" + usr + " 年龄:" + age;let str = `<a href='www.baidu.com'>姓名:${usr}</a> 年龄:${age}函数调用结果为:${tst()}运算结果为:${age - 3}`;console.log(str);function tst() {return 'Hello web';}//输出结果/*<a href='www.baidu.com'>姓名:李四</a> 年龄:21 函数调用结果为:Hello web 运算结果为:18*/
数组扩展:Array.from(v) 将伪数组对象或可遍历对象(必须要有 length 属性)转换为真数组 ;
<body> <div></div> <div></div> <div></div><script>let divs = document.getElementsByTagName('div');console.log(divs, 888);let arr = Array.from(divs);console.log(arr);let obj = {0: 'a',1: 'b',2: 'c',length: 3 //注意这里};console.log(obj);let arr2 = Array.from(obj);console.log(arr2);</script></body>
扩展运算符语法
主要功能
类似于arguments,收集数据功能 ;
类似于apply,数据展开功能;
<script>//扩展运算符语法: ... //扩展去处符的功能:类似于arguments[收集数据]、类似apply展开数据功能//类似apply展开数据功能let arr = [1, 2, 3];// let arr2 = [7, 9, ...arr];let arr2 = [...arr, 7, 9];console.log(arr2);let obj = {"x": 1,"y": 3};let obj2 = {fn: function () {console.log('Hello')}}let obj3 = {"data": {...obj},"method": {...obj2}};// console.log(obj3);//类似于arguments[收集数据]function demo(...x) { //收集数据console.log(x, 999);// test(x[0], x[1], x[2]);test(...x); //展开数据// test(x) //解构赋值}function test(m, n, z) {// function test([m, n, z]) { //解构赋值console.log(m, n, z, 777);}demo(2, 3, 4);</script>
扩展运算符应用;
实现数组的合并,箭头函数的参数收集(...arugs)=>{} ,因为箭头函数中没有arguments伪数组收集参数;
<script>let arr = [1, 3, 5, 6, 7];let [, x, y, ...z] = arr;// console.log(x, y, z);let str = "hello";let arr2 = [...str];console.log(arr2);</script>
Symbol数据类型
是es6 新增的数据类型,和原有的数据类型(String、Number、Boolean、Object、null、undefined)一样。
let s = Symbol('123');console.log(s,typeof s);// Symbol(123) "symbol
Symbol类型的特点:唯一性、不能参与运算(包括字符串拼接)、for...in或for...of不能遍历;
声明方式:使用Symbol()函数申明创建变量;
不能使用new 关键字创建;Symbol is not a constructor;
var s1 = Symbol();var s2 = Symbol();//两者一定不相等console.log(s1, s2, s1 === s2); //Symbol() Symbol() false// ============================// var s3 = Symbol('22');var arr = [1, 2, 32];var band = Symbol(['品牌']);console.log(band + 1); //Cannot convert a Symbol value to a numberconsole.log(band + '1'); // Cannot convert a Symbol value to a string//数组存入的此数据可以遍历// console.log(band);arr.push(band);console.log(arr); // [1, 2, 32, Symbol(品牌)]// for (let i = 0; i < arr.length; i++) {// console.log(arr[i]); // 1 2 32 Symbol(品牌)// }for (let x of arr) {console.log(x); // 张三 22}// ===========================var o = {name: '张三',age: '22'}o[band] = '苹果';// console.log(o); // {name: "张三", age: "22", Symbol(品牌): "苹果"}for (let k in o) {// console.log(o[k]); // 张三 22}
Symbol.for(key):相同的key,产生的symbol值是相同的;
var a = Symbol.for("apple");var b = Symbol.for("apple");console.log(a === b); //true
Set数据类型
语法:对应Array数据结构
- let curset = new Set([1, 3, 5, 3, 7, 5, 9]);
- console.log(curset); //Set(5) {1, 3, 5, 7, 9}
特点:Set类似于数组,但与数组不同的是set中的成员没有重复的。
方法:Set原型提供了这些方法:add()、delete()、has()、clear()、 size;
- let curset = new Set([1, 3, 5, 3, 7, 5, 9]);
- console.log(curset); //Set(5) {1, 3, 5, 7, 9}
- curset.add(99);
- curset.delete(9);
- console.log(curset.has(100)); //false
- console.log(curset.size) //5
- console.log(curset); //Set(5) {1, 3, 5, 7, 99}
应用:实现数组去重;
- //使用set实现数组去重:
- let curset = new Set([1, 3, 5, 3, 7, 5, 9]);
- // 方法一
- function uniqueval(arr) {
- let sets = new Set(arr);
- return Array.from(sets);
- }
- // 方法二
- var uniqueval = arr => Array.from(new Set(arr));
- let arrs = uniqueval([1, 2, 3, 1, 3, 5]);
- console.log(arrs);
map数据类型
语法:对应Object对象结构
特点:他的键名可以是任何数据类型;
map和object对象不一样,map可以使用forEach遍历,object不可以,这一点map的遍历形式与set、array是一致。map的key可以允许es已知的数据类型,object对象的key就是一个字符串(还有symbol);所有综合考虑来说map 的功能相比object更强大。但是现在的开发场景下map的作用很小,因为我们请求数据都是JSON格式,JSON.stringify和JSON.parse已经与object可以有效的转换了,map暂无提供,不太方便;map目前没有提供解构赋值的做法,也不太方便。
方法: Map原型提供了一些方法: set()、get()、delete()、has()、clear()、size ;
- let curMap = new Map();
- // 设置
- curMap.set('1', '你好');
- console.log(curMap); //Map(1) {"1" => "你好"}
- // 获取
- console.log(curMap.get('1')); // 你好
- console.log(curMap.size); // 1
- console.log(curMap.has('你好')); //是否存在 '你好' 的键名
- console.log(curMap.delete('1')); // true
箭头函数
1. 箭头函数的定义
//语法: ([参数...])=>{函数体}var arrowFn = (n) => {console.log('箭头函数');}arrowFn(); // 箭头函数
2. 用途:箭头函数通常用于回调函数,回调函数通常都是匿名函数,因些箭头函数可以简化匿名函数;
3. 区别于一般函数的三个特点:
一般函数可以先调用后定义,而箭头函数只能先定义后调用;
fn(); //普通函数function fn() {console.log('普通函数');}fn(); //普通函数arrowFn(); //TypeError: arrowFn is not a functionvar arrowFn = (n) => {console.log('箭头函数');}arrowFn(); // 箭头函数
一般函数有arguments、而箭头函数没有arguments,但可以使用rest参数;(扩展运算符)
fn(); //普通函数function fn() {console.log('普通函数', arguments);}var arrowFn = (n) => {console.log('箭头函数', arguments); //arguments is not defined}arrowFn(); // 箭头函数//使用 rest参数var arrowFn = (...n) => {console.log('箭头函数', n); // 箭头函数 [1, 2, 3]}arrowFn(1, 2, 3);
一般函数可以当作构造函数,而箭头函数不能当作构造函数,因为箭头函数没有自己this;
function fn() {console.log('普通函数');}new fn(); // 普通函数var arrowFn = () => {console.log('箭头函数');}new arrowFn(); // arrowFn is not a constructor
箭头函数的简写形式:
当箭头函数只有一个形参,外边小括号()可以省略;
当箭头函数的函数体中只有一条语句,并且该条语句作为函数的返回值,那么return关键字和花括号{}狗可以省略;
var fn = (n) => {return n + 3;}// 简写为:var fn = n => n + 3;console.log(fn(2)); //5
箭头函数中的this指向:箭头函数中的this指向依赖外层函数中的this指向,与当前调用者无关;
promise对象
什么是Promise?
Promise 实际上是一个许诺器,里面的代码通常是将来要执行的代码,而这些代码一般都是异步的I[input:输入]/O[output:输出]操作,这些操作执行完成后会有两种结果:成功或失败,因些promise有三种状态:初始状态[pending]、成功状态[resolve]、失败状态[reject];
promise对象解决 回调嵌套问题:回调地狱问题;
回调嵌套缺点:当业务复杂时代码可读性、可维护性变差。
例如:多次请求ajax 数据;使用Promise对象解决不断嵌套函数的问题;
多级菜单请求数据:
// var prs = new Promise(回调函数);//resolve成功状态的回调函数;//reject 失败状态的回调函数;var prs = new Promise((resolve,reject)=>{//将来要执行的代码;异步代码setTimeout(function(){...});});
Promise的错误处理:
错误处理方式一:then()方法中的第二个函数只能获取到失败状态返回值,不能处理成功状态下的业务处理错误信息,但是可以使用try..catch来解决;
var p = new Promise((resolve, reject) => {setTimeout(function () {// console.log(p);// resolve('成功');try {alt();} catch (error) {console.log(error); //捕获错误reject(error);}}, 1000);});// 方式一: then方法中的reject方法不能捕捉到promise对象中代码的错误,也不能捕捉到成功回调函数中的错误; 但是可以使用try...catch 语句来捕获错误p.then((d) => {console.log(d);}, (e) => {console.log(e);});
错误处理方式二:catch()方法不但可以获取失败状态返回值,而且还可以拿到成功状态下的业务错误信息;
var p = new Promise((resolve, reject) => {setTimeout(function () {// console.log(p);resolve('成功');// try {// alt();// } catch (error) {// console.log(error);// }}, 1000);});// 方式二: catch()方法可捕捉带成功回调函数中的错误;但是不能捕捉到promise对象中代码的错误,它只能通过try...catch 语句来捕获错误;p.then((d) => {alt();console.log(d);}).catch(e => {console.log(e);});
promise的链式传递;因为then() 方法的返回值 是一个promise对象;
成功的回调函数可以调用多次;
失败的回调函数只会执行一次;
Promise的all()方法与race()方法:
Promise.all()并发方法的特点:
要么所有Promise实例都成功、要么都失败;
如果都成功则返回所有Promise实例结果,并且该结果是数组,数组中的元素与all()方法中的值与Promise实例中的一一对应;
如果有一个Promise实例执行失败则都失败,并且只返回最先失败的那个Promise实例结果。
Promise.race()方法的特点:
哪个promise实例最先执行完成返回哪个promise的结果,不管这个结果是resolve还是reject,只比谁最快;
for..of..循环
迭代器 iterator
什么是iterator?
iterator中文为迭代器或遍历器,它是一种接口机制,只要提供了这种接口机制的数据类型都可以使用for...of来遍历,这些数据类型都可以使用for...of来遍历:Array、String、arguments、Set、map。数组原来可以使用for或forEach来遍历;对象原来可以使用for...in来遍历;Set、map原来可以使用forEach来遍历;
但是,只要对象类型实现了iterator 接口就可以使用for...of来遍历数据;接口:Symbol.iterator;
iterator接口使用: 在下面的代码中for...of...循环相当于调用了next方法;
迭代器工作原理:
使用数组中的迭代器 next的方法 返回一个对象{value: 值/undefined,done: false/true} 值和为done的布尔值;
- console.log(new Array);//有一项: Symbol(Symbol.iterator): ƒ values()
- // 使用此方法来实现 for...of 循环 原理:
- let arr = [1, 2, 12, 9, 43];
- let arr_iterator = arr[Symbol.iterator](); //ƒ values() { [native code] }()
- //数组迭代器 中的 next 方法
- console.log(arr_iterator); //Array Iterator {} ==>>> next()
- console.log(arr_iterator.next()); // {value: 1, done: false}
- console.log(arr_iterator.next()); // {value: 2, done: false}
- console.log(arr_iterator.next()); // {value: 12, done: false}
- console.log(arr_iterator.next()); // {value: 9, done: false}
- console.log(arr_iterator.next()); // {value: 43, done: false}
- console.log(arr_iterator.next()); // {value: undefined, done: true}
- // 完成遍历
- // ====================================
- for (let value of arr) {
- // 每一次循环的值 of 数组
- console.log(value);
- }
let arr = [1, 2, 12, 9, 43];function fn() {var index = 0;return {next: function () {if (index < arr.length) {return {value: arr[index++],done: false}} else {return {value: undefined,done: true}}}}}let obj = fn();console.log(obj.next()); // {value: 1, done: false}console.log(obj.next()); // {value: 2, done: false}console.log(obj.next()); // {value: 12, done: false}console.log(obj.next()); // {value: 9, done: false}console.log(obj.next()); // {value: 43, done: false}console.log(obj.next()); // {value: undefined, done: true}
给对象实现iterator接口,实现接口中的next() 方法,返回一个对象的值{value:xxx, done, false/true};可以使用 for..of 遍历数据 obj[Symbol.iterator]=function(){} 然后接口中的实现next方法;
//给obj对象部署iterator接口:let obj = {name: '张三',age: '32'}// console.log(obj);//给obj对象部署iterator接口:obj[Symbol.iterator] = function () { //获取对象中所有属性名let keys = Object.keys(obj);// console.log(keys);let i = 0;return {next: function () {// console.log(i);if (i < keys.length) {return {value: obj[keys[i++]],done: false}} else {return {value: undefined,done: true}}}}}var o = obj[Symbol.iterator]();console.log(o.next()); // {value: "张三", done: false}console.log(o.next()); // {value: "32", done: false}console.log(o.next()); // {value: undefined, done: true}for (let v of obj) {console.log(v); // 张三 32}
for...of与forEach,for..in 的区别?
forEach中不能使用break、continue、return,但 for...of 中则可以使用。
for...in 通常遍历对象,for...of 可以遍历数组、string、arguments、set、map。
Generator函数
1. 什么是Generator函数?
Generator函数是一个函数,可以在函数内部通过yield来定义多个状态,因此generator函数也称为状态机,它是异步编程解决方案之一。
2. Generator函数定义:
function* 函数名() {yield 异步操作1;yield 异步操作2;}
3. Generator函数特点: (5个)
function 与函数名之间有一个星号;
内部用yield表达式来定义不同的状态;
调用generator函数返回的是iterator对象,但不会执行函数内部逻辑;
调用next方法之后,函数内部逻辑开始执行,遇到yield表达式停止,返回对象{value: yield后的表达式结果/undefined, done: false/true};
再次调用next方法会从上一次停止时的yield处开始,直到最后,yield语句返回结果通常为 {value: undefined,done: true } ;
注意:当给next()方法传参时会作为重启yield语句的返回值。
4. Generator函数应用:异步编程请求数据,渲染页面;将generator类型的函数封装给对象,实现接口,可以使对象实现for...of方法;
// 实现接口;Object.prototype[Symbol.iterator] = function* () {for (let i in this) {yield this[i];}}//--------------Genertaor函数本身会返回具备Iterator接口的对象function* iterEntries(obj) {// 获取所有的属性名;let keys = Object.keys(obj);for (let i = 0; i < keys.length; i++) {let key = keys[i];yield [key, obj[key]];//每一次 调用next方法 执行停止的位置;}}let myObj = { foo: 3, bar: 7 };for (let [key, value] of iterEntries(myObj)) {//相当于调用了next方法;console.log(key, value);}
async函数和await关键字
1. async简介?
async是基于Promise的generator语法糖,以同步流程表达异步操作;
2. async函数语法:
async function 函数名(){await 异步操作1;await 异步操作2;}
3.async函数的特点:
被async修饰过的函数调用,返回的是一个promise对象,对象的状态默认是 '完成状态 resolve';
async函数会根据当前状态自动调用并且返回值,无需手动调用resolve() 和 reject() 方法;
然后使用函数对象.then()方法处理返回的结果值;
4.await的特点:
不能单独使用,需要和 async 一起使用;
遇到await可以让程序暂停执行,等待promise的执行结果;
await可以直接处理Promise的resolve()结果,对Promise的reject()结果我们有两种处理方式:
第一种解决方式:在promise对象中使用catch()方法
第二种解决方式:在promise中不管成功与否都调用resolve()方法,通过resolve()方法传参来区别是成功或失败;
es6中的类
面向过程与面向对象
面向过程(吃蛋炒饭):买食材 --> 洗食材 --> 开始做饭 --> 吃饭 --> 洗刷
面向对象(吃蛋炒饭):打开app --> 选好商家并下单 --> 商家开始加工 --> 外卖小哥送餐 --> 开始吃饭
优点:面向对象相对面向过程来说可以提高代码重用度、降低系统耦合性;
不同编程语言实现面向对象的方式不太一样,比如java、php采用的“类”来实现的,javascript采用的“原型”来实现 的。
什么是类?
类是具有相同属性和行为(方法)的一类事务集合,比如:人类、电脑、 汽车;
什么是对象?
对象是类中某个具体的个体,比如:张三这个人、我的电脑、你的汽车;
定义类的语法:
class 类名{constructor(){// 构造器}//属性//方法}///=============================================class Star {// 属性方法的集合// 构造器:当实例化类时会自动被调用constructor(names, age) {this.age = age;this.names = names;}sing() {console.log(this.names + '唱歌');}dance() {console.log(this.age + '岁的' + this.names + '跳舞');}}
注意:构造器方法在类实例化时会被自动调用
成员属性和成员方法:
成员属性和方法:在每创建一个对象时都会将类中的属性及方法拷贝一份,每个对象中都具有类中基本属性和方法,这样属性和方法称为成员属性和方法,成员属性和方法属于对象,而不属于类。
静态属性和静态方法:
静态属性和方法是归类所有,全局独一份,在静态方法中只能访问静态属性,当然在成员方法中只能访问成员属性,静态方法直接通过类名来访问,不需要实例化。
注意:静态方法中this指向类本身,而成员方法中的this指向当前对象。
类的继承语法:
类名 extends 父类 {}
类的继承注意事项:
1)关于构造器方法:
(1)、当子类中没有定义构造方法时默认访问父类的构造方法
(2)、当子类中定义了构造方法时要调用super()方法,通过super()方法可以把父类中的this继承下来的同时也可以给父类中的属性设置属性值;(需要先调用super()方法之后才可以调用自己;) ;
2)方法重构/重写?
(1)为什么要使用方法重构/重写?
当父类中的方法在子类中不能满足需求时要对父类中的方法进行重构/重写.
(2)怎样进行方法重构/重写?
在子类定义和父类同名的方法即可。
当子类中和父类中的方法重名,调用父类中的方法需要super.方法名();来调用;
类的私有属性和方法;
只能在类中访问,不能通过类名直接访问;
构造方式:通过Symbol来定义数据类型,然后保证数据的唯一性,然后通过闭包返回类,而不返回属性,就访问不到属性了,实现属性的私有化;
let _users = Symbol('姓名');let _ages = Symbol('年龄');class Test {//属性constructor(users, ages) {// this.user = users;// this.age = ages;this[_users] = users;this[_ages] = ages;}//方法}let lisi = new Test('李四', 20);// console.log(lisi.user, lisi.age);// _users = Symbol('姓名');//问题:一旦知道了symbol的变量名:_users,这样还可以访问属性值// console.log(lisi[_users]);let mytest = (function () {let _users = Symbol('姓名');let _ages = Symbol('年龄');class Test {//属性constructor(users, ages) {// this.user = users;// this.age = ages;this[_users] = users;this[_ages] = ages;}//方法getAge() {return this[_ages];}getUser() {return this[_users];}}return Test;})();// console.log(mytest);let zhangsan = new mytest('张三', 19);// console.log(zhangsan[_ages]);console.log(zhangsan.getAge());
当然还有一些没有涉及到的,但上述部分都是比较常用到的,而且面试时候也会经常提及的一些基础知识,不管怎么样,都需要理解掌握;