ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
ES6主要的新特性如下所示:
let 关键字用来声明变量,使用 let 声明的变量有几个特点:
const 关键字用来声明常量,const 声明有以下特点:
注意: 对象属性修改和数组元素变化不会触发 const 错误
应用场景:声明对象类型使用 const,非对象类型声明选择 let
5. 如果const的是一个对象,对象所包含的值是可以被修改的。抽象一点儿说,就是对象所指向的地址没有变就行:
const student = { name: 'cc' }
student.name = 'yy';// 不报错
student = { name: 'yy' };// 报错
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
tmp
,但是块级作用域内let
又声明了一个局部变量tmp
,导致后者绑定这个块级作用域,所以在let
声明变量前,对tmp
赋值会报错。在日常开发中,建议是全面拥抱let/const,一般的变量声明使用let关键字,而当声明一些配置项(类似接口地址,npm依赖包,分页器默认页数等一些一旦声明后就不会改变的变量)的时候可以使用const,来显式的告诉项目其他开发者,这个变量是不能改变的(const声明的常量建议使用全大写字母标识,单词间用下划线),同时也建议了解var关键字的缺陷(变量提升,污染全局变量等),这样才能更好的使用新语法。
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。
解构赋值是对赋值运算符的扩展。它是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。
let [a,b,c] = [1,2,3];
console.log(a,b,c);//1,2,3
**************************
let [a,b,c] = [1,,3];
console.log(a,b,c);//1,undefined,3
**************************
let [a,,b] = [1,2,3];
console.log(a,b);//1,3
**************************
let [a,..b] = [1,2,3];//...是剩余运算符,表示赋值运算符右边除第一个值外剩余的都赋值给b
console.log(a,b);//1,[2,3]
对象的解构赋值和数组类似,不过左边的变量名需要使用对象的属性名,并且用大括号{}而非中括号[]:
let obj = {name:'ren',age:12,sex:'male'};
let {name,age,sex} = obj;
console.log(name,age,sex);//'ren' 12 'male'
let {name:myName,age:myAge,sex:mySex} = obj;//自定义变量名
console.log(myName,myAge,mySex);//'ren' 12 'male'
模板字符串(template string)是增强版的字符串,用反引号(`)标识,特点:
// 定义字符串
let str = `
- 沈腾
- 玛丽
- 魏翔
- 艾伦
`;
// 变量拼接
let star = '王宁';
let result = `${star}在前几年离开了开心麻花`;
注意:当遇到字符串与变量拼接的情况使用模板字符串
ES6 允许使用「箭头」(=>)定义函数。
/**
* 1. 通用写法
*/
let fn = (arg1, arg2, arg3) => {
return arg1 + arg2 + arg3;
}
/**
* 2. 省略小括号的情况
*/
let fn2 = num => {
return num * 10;
};
/**
* 3. 省略花括号的情况
*/
let fn3 = score => score * 20;
/**
* 4. this 指向声明时所在作用域中 this 的值
*/
let fn4 = () => {
console.log(this);
}
箭头函数的注意点:
注意:1. 箭头函数不会更改 this 指向,用来指定回调函数会非常合适
2.箭头函数和普通函数最大的区别在于其内部this永远指向其父级AO对象的this。
- 普通函数在预编译环节会在AO对象上添加this属性,保存一个对象。每个普通函数在执行时都有一个特定的this对象,而箭头函数执行时并不直接拥有this属性,如果你在箭头函数中使用this,将根据函数作用域链,直接引用父级AO对象上this绑定的对象。普通函数的AO对象只有在函数执行时才产生,换言之,普通函数的this是由函数执行时的环境决定。而箭头函数的特别之处在于,当函数被定义时,就引用了其父级AO对象的this,即箭头函数的this由定义时的环境决定。
// ES6之前,当未传入参数时,text = 'default';
function printText(text) {
text = text || 'default';
console.log(text);
}
// ES6;
function printText(text = 'default') {
console.log(text);
}
printText('hello'); // hello
printText();// default
Promise作为ES6中推出的新的概念,改变了JS的异步编程,现代前端大部分的异步请求都是使用Promise实现,fetch这个web api也是基于Promise的,简述一下之前统治JS异步编程的回调函数,回调函数有什么缺点,Promise又是怎么改善这些缺点。
针对回调函数这么多缺点,ES6中引入了一个新的概念Promise,Promise是一个构造函数,通过new关键字创建一个Promise的实例,来看看Promise是怎么解决回调函数的这些问题。
let promise = new Promise((resolve,reject)=>{
setTimeout(() => {
resolve('I have been resolved')
}, 2000);
})
promise.then(res=>{
console.log(res)//2秒后打印字符串
})
不清楚回调是否都是异步调用的(可以同步调用ajax,在收到响应前会阻塞整个线程,会陷入假死状态,非常不推荐)
Promise在设计的时候保证所有响应的处理回调都是异步调用的,不会阻塞代码的执行,Promise将then方法的回调放入一个叫微任务的队列中(MicroTask),确保这些回调任务在同步任务执行完以后再执行。
es6允许当对象的属性和值相同时,省略属性名
let x = 0
let obj = {
x //es6允许省略x:
}
let bar = () => ({ x: 4, y: 5, z: 6 })
let { x: x, y: y, z: z } = bar()
//简写为
let { x, y, z } = bar()
x //4
y //5
z //6
结合上文的解构赋值,这里的代码会其实是声明了x,y,z变量,因为bar函数会返回一个对象,这个对象有x,y,z这3个属性,解构赋值会寻找等号右边表达式的x,y,z属性,找到后赋值给声明的x,y,z变量
es6允许当一个对象的属性的值是一个函数(即是一个方法),可以使用简写的形式
let obj = {
func: function () {
}
}
// es6方法的简写
let obj2 = {
func() {
}
}
//使用箭头函数进行简化
let obj3 = {
func: () => {
}
}
剩余/扩展运算符同样也是ES6一个非常重要的语法,使用3个点(…),后面跟着一个含有iterator接口的数据结构
以数组为例,使用扩展运算符使得可以"展开"这个数组,可以这么理解,数组是存放元素集合的一个容器,而使用扩展运算符可以将这个容器拆开,这样就只剩下元素集合,你可以把这些元素集合放到另外一个数组里面, 代替ES3中数组原型的concat方法
let arr1 = [1, 2, 3]
let arr2 = [4, 5, 6]
console.log(arr1.concat(arr2)) //[1, 2, 3, 4, 5, 6]
console.log([...arr1, ...arr2]) //[1, 2, 3, 4, 5, 6]
剩余运算符最重要的一个特点就是替代了以前的arguments;rest只是形参, 可以随意取名
function func(a, b, c) {
console.log(arguments[0], arguments[1], arguments[2])
}
func(1, 2, 3)
// rest是形参,承载了所有的函数参数,可以随意取名
function func1(...rest){
console.log(rest) //[1, 2, 3]
}
func1(1,2,3)
剩余运算符可以和数组的解构赋值一起使用,但是必须放在最后一个,因为剩余运算符的原理其实是利用了数组的迭代器,它会消耗3个点后面的数组的所有迭代器,读取所有迭代器生成对象的value属性,剩运算符后不能在有解构赋值,因为剩余运算符已经消耗了所有迭代器,而数组的解构赋值也是消耗迭代器,但是这个时候已经没有迭代器了,所以会报错
let [first, ...arr] = [1, 2, 3, 4, 5]//success
first //1
arr //[2,3,4,5]
let [...arr, last] = [1, 2, 3, 4, 5] //Uncaught SyntaxError:Rest element must be last element
这里first会消耗右边数组的一个迭代器,…arr会消耗剩余所有的迭代器,而第二个例子…arr直接消耗了所有迭代器,导致last没有迭代器可供消耗了,所以会报错,因为这是毫无意义的操作
剩余运算符和扩展运算符的区别就是,剩余运算符会收集这些集合,放到右边的数组中,扩展运算符是将右边的数组拆分成元素的集合,它们是相反的
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
知识点:
class Animal {
// 构造函数,实例化的时候将会被调用,如果不指定,那么会有一个不带参数的默认构造函数.
constructor(name,color) {
this.name = name;
this.color = color;
}
// toString 是原型对象上的属性
toString() {
console.log('name:' + this.name + ',color:' + this.color);
}
}
var animal = new Animal('dog','white');//实例化Animal
animal.toString();
console.log(animal.hasOwnProperty('name')); //true
console.log(animal.hasOwnProperty('toString')); // false
console.log(animal.__proto__.hasOwnProperty('toString')); // true
class Cat extends Animal {
constructor(action) {
// 子类必须要在constructor中指定super 函数,否则在新建实例的时候会报错.
// 如果没有置顶consructor,默认带super函数的constructor将会被添加、
super('cat','white');
this.action = action;
}
toString() {
console.log(super.toString());
}
}
var cat = new Cat('catch')
cat.toString();
// 实例cat 是 Cat 和 Animal 的实例,和Es5完全一致。
console.log(cat instanceof Cat); // true
console.log(cat instanceof Animal); // true
遍历器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。
ES6 提供了新的数据结构 Set(集合)。它类似于数组,但成员的值都是唯一的,集合实现了 iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历,集合的属性和方法:
size 返回集合的元素个数
add 增加一个新元素,返回当前集合
delete 删除元素,返回 boolean 值
has 检测集合中是否包含某个元素,返回 boolean 值
clear 清空集合,返回 undefined
//创建一个空集合
let s = new Set();
//创建一个非空集合
let s1 = new Set([1, 2, 3, 1, 2, 3]);
//集合属性与方法
//返回集合的元素个数
console.log(s1.size);
//添加新元素
console.log(s1.add(4));
//删除元素
console.log(s1.delete(1));
//检测是否存在某个值
console.log(s1.has(2));
//清空集合
console.log(s1.clear());
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。但是“键” 的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也实现了iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。Map 的属性和方法:
size 返回 Map 的元素个数
set 增加一个新元素,返回当前 Map
get 返回键名对象的键值
has 检测 Map 中是否包含某个元素,返回 boolean 值
clear 清空集合,返回 undefined
//创建一个空 map
let m = new Map();
//创建一个非空 map
let m2 = new Map(
[
['name', '张三'],
['slogon', '李四']
]);
//属性和方法
//获取映射元素的个数
console.log(m2.size);
//添加映射值
console.log(m2.set('age', 6));
//获取映射值
console.log(m2.get('age'));
//检测是否有该映射
console.log(m2.has('age'));
//清除
console.log(m2.clear());
let arr = [1,2,3,4,5,6,7];
/*
* 没有返回值,只针对每个元素调用func。
* 优点:代码简介。
* 缺点:无法使用break,return等终止循环
*/
//ES5写法
arr.forEach(function(item,index){
console.log(item); // 1 2 3 4 5 6 7
})
//ES6写法
arr.forEach((item,index) => {
console.log(item); // 1 2 3 4 5 6 7
return item
})
let arr = [1,2,3,4,5,6,7];
/*
* 有返回值,返回一个新的数组,每个元素为调用func的结果。
*/
let newArr = arr.map((item,index) => {
return item*2
})
console.log(newArr); // [2,4,6,8,10,12,14]
let arr = [1,2,3,4,5,6,7];
/*
* 返回一个Boolean,判断是否有元素符合func,如果有一个符合条件,就会终止循环,返回true。
*/
arr.some((item,index) => {
return item > 5; // true
})
let arr = [1,2,3,4,5,6,7];
/*
* 返回一个Boolean,判断每一个元素是否都符合func,如果有一个不符合,就会终止循环,返回false。
*/
arr.every((item,index) => {
return item < 10; //true
})
let arr = [1,2,3,4,5,6,7];
/*
* 有返回值,返回一个符合func条件的数组的集合
*/
let newArr = arr.filter((item,index) => {
return item > 3;
})
console.log(newArr); // [4,5,6,7]
let arr = [1,2,3,4,5,6,7];
/*
* 让数组中的前项和后项做某种运算,并返回运算结果
*/
let res = arr.reduce((prev,next) => {
return prev + next;
})
console.log(res); // 28
let arr = [1,2,3,4,5,6,7];
/*
* 不创建新数组,不改变元素组
* 在判断中一旦某个元素符合func,立马跳出循环,返回当前符合条件的元素
*/
let res = arr.find((item,index) => {
console.log( 'arr[' + index + '] = ' item ); // arr[3] = 4
return item > 3;
})
console.log(res); // 4
ES6提供了二进制和八进制的新的写法,分别用前缀0b和0o表示
Number.isFinite()
用来检查一个数值是否为有限的Number.isNaN()
用来检查一个值是否为NaNES6 将全局方法 parseInt 和 parseFloat,移植到 Number 对象上面,使用不变
用于去除一个数的小数部分,返回整数部分。
Number.isInteger()
用来判断一个数值是否为整数
ES6新增了一些Object
对象的方法
Object.is
比较两个值是否严格相等,与[===]行为基本一致Object.assign
对象的合并,将源对象的所有可枚举属性,复制到目标对象