ECMAScript 6(下面简称ES6)已于2015年6月正式发布,它提供的许多新的语法特性,将会成为Javascript语言新的标准。

ES6发布至今,许多浏览器的最新版本对ES6大部分特性都实现了,但是没有浏览器对ES6全部特性能够完美的支持,可以通过http://kangax.github.io/compat-table/es6/查看浏览器的支持情况。如果想要更好的体验ES6的新特性和新语法,可以使用转换器,将ES6转换成ES5。

ES6的出现,给前端开发者们带来不一样的体验,使用ES6可以更加方便的实现很多复杂的功能,提高前端开发者的效率。这篇博客将会介绍ES6比较常用的语法特性,希望能够给大家带来收获。在线测试代码可以点击这里

目录

1.let和const
2.箭头函数
3.字符串
4.解构赋值
5.函数参数
6.类 class
7.Symbols
8.for…of循环
9.Set和Map数据解构
10.Promise
11.Generators生成器


1.let和const

letconst是ES6新增的声明变量的命令。使用方法与var相似,不过letconst声明的变量只能在所在的代码块使用,也就是块级作用域。

Const
const即constant,用来声明只读的常量,一旦声明就必须赋值,声明常量之后不可赋值,但是可以继续添加属性。
(1)const声明常量时必须赋值,否则报错:

const Pi;  // Missing initializer in const declaration

(2)由于const声明的Pi是常量,如果初始化后对其赋值,则报错:

const Pi = 3.14;
Pi = 3; //Assignment to constant variable.

(3)常量obj是一个对象,可以对其添加属性,但不可以再次赋值:

const obj = {};
obj.name = "张三";
console.log(obj); // {name: "张三"}
obj = {}; //Assignment to constant variable.

let
let其与var相似,它们的不同体现在三点:
(1)不存在变量提升
使用var

var str = "hello";
function getStr(arg) {
if (arg) {
var str = 'hello world';
return str;
}
return str;
}
console.log(getStr(false)); //undefined

使用let:

let str = "hello";
function getStr(arg) {
if (arg) {
let str = 'hello world';
return str;
}
return str;
}
console.log(getStr(false)); //hello

第一个例子中,由于if内声明的变量会被提升到函数头部,所以返回了undefined。第二个例子由于let声明的变量不会发生变量提升,if内声明的变量只能在当前所在的代码块使用,所以直接返回hello。

(2)暂时性死区
let声明的变量只能在代码块中使用,同时必须在声明后才能使用,否则会报错,这就是暂时性死区。

if(true){
console.log(str); //ReferenceError: str is not defined
let str = "hello";
}

let命令声明str之前,上面的代码都属于str的”死区”。
如果将上面的let变成var,则返回undefined:

if(true){
console.log(str); //undefined
var str = "hello";
}

(3)不允许重复声明
let不允许在相同的作用域内,重复声明同一个变量。而var重复声明变量,则后面的变量会覆盖前面声明的变量。

if(true){
let str = "hello";
console.log(str);

let str = "world";
console.log(str); //SyntaxError: Identifier 'str' has already been declared
}

因此,对于constletvar三个命令,当想要定义常量的时候可以选择const。当声明一个变量赋值后还会修改的可以选择letvar,当使用于循环计数或者算法,建议优先选择let


2.箭头函数

当我们使用闭包的时候,函数内部的this总是发生改变,不能指向我们所预期的对象。而箭头函数的出现,正是可以让函数里面this指向预期的对象。另外,箭头函数的出现,还可以让我们的代码量大大减少。
箭头函数即使用箭头“=>”定义函数,用法如下:

let result = [1,2,3,4].map(i=>i*i);

箭头的左边是输入的参数,右边是进行的操作以及返回的值。
上面代码等同于es5:

let rusult = [1,2,3,4].map(function(i){
return i*i;
})

从上两段代码中可以看出,箭头函数在一定程度上可以减少代码量,使得我们的代码更加简洁。
(1)如果函数不需要参数或者需要多个参数,需使用用圆号。

let result = () => 5;

let result2 = [1,2,3,4].map((i,item)=>console.log(i:item));

(2)如果箭头右边执行多行命令,则需使用大括号,并使用return返回。

let result = [1,2,3,4].map(i=>{return i*i;})

(3)如果返回的是对象,则需在大括号外层添加中括号。

let result = i => ({id :i ,name : "cici"})

(4)可代替立即执行函数。

(x => x*2)(3);      //6

(5)在箭头函数,this指向的是定义时所在的对象,而不是使用时所在的对象。
例子:

//ES5
function Person(data){
this.name = data.name;
this.age = data.ag;
this.getInfo = function(){
console.log(this.name + " " + this.age);
}
this.sayHello = function(){
setTimeout(function(){
console.log(this);
},1000)
}
}

let dudu = new Person({
name : "dudu",
age : 20
});

dudu.getInfo(); //dudu 20
dudu.sayHello(); //windows

在超时调用的代码都是在全局作用于中执行,所以函数中的this的值指向了window对象。而箭头函数可以保持作用域,保证this的指向不会变:

 function Person(data){
this.name = data.name;
this.age = data.age;
this.getInfo = function(){
console.log(this.name + " " + this.age);
}
this.sayHello = function(){
setTimeout(() => console.log(this),1000)
}
}

let dudu = new Person({
name : "dudu",
age : 20
});

dudu.getInfo(); //dudu 20
dudu.sayHello(); //Person {name: "dudu", age: 20}

在sayHello函数中,使用了箭头函数,当前作用域是在person对象的一个方法中,箭头函数生成的临时函数的作用域也就是person对象的作用域。


3.字符串

在ES6中,添加了许多对于字符串的接口,使得更加的便利地去处理字符串。
(1) 模版字符串
以前我们需要拼接字符串跟变量时,需要使用“+”进行拼接。而模板字符串的出现可以让变量嵌入到字符串中,可以让代码变得更加简单。同时,模板字符串还可保留换行符空白符,可以让字符串的定义更加方便。

a.可以在字符串中是使用变量

let name = "dudu";
let age = 20;
console.log(`my name is ${name},i am ${age} years old`); // "my name is dudu,i am 20 years old"

在模版字符串中,使用${}包含变量。也可在变量进行算法运算或执行函数等操作。

let a = 5;
let b = 10;
console.log(`5加10等于${a+b},5乘10等于${a*b}`) //5加10等于15,5乘10等于50


function add(x,y){
return x+y;
}
console.log(`5加10等于${add(a,b)}`) //5加10等于15

b.字符串换行符空白符被保留
在ES6之前:

let str = "hi,"
+ "i am dudu"
+ "i am 20 years old";

console.log(str); //hi,i am dudui am 20 years old

ES6:

let str2 = `hi,
i am dudu,
i am 20 years old`;
console.log(str2);
//hi,
//i am dudu,
// i am 20 years old

通过上面的示例可以看出,模版字符串的出现给字符串的使用带了很大的便利。

(2)includes、startsWith、endsWith
之前我们是通过indexOf来判断字符串是否包含某个字符串:

let str = "hello world";
console.log(str.indexOf("o")>-1);

而在ES6中,增加了三个新方法的进行检索。
includes判断字符串是否包含某字符串,返回布尔值。
startsWith判断字符串是否以某字符串开头,返回布尔值。
endsWith判断字符串是否以某字符串结尾,返回布尔值。

let str = "hello world";
console.log(str.includes("wor")); //true
console.log(str.startsWith("he")); //true
console.log(str.endsWith("ld")); //true

console.log(str.includes("llo",1)); //true
console.log(str.includes("llo",5)); //false

console.log(str.startsWith("llo",2)); //true
console.log(str.startsWith("llo",3)); //false

console.log(str.endsWith("llo",5)); //true
console.log(str.endsWith("llo",9)); //false

在上面代码中,加入了第二个参数。includesstartsWith的第二个参数代表搜索的开始位置。而endsWith代表了只搜索字符串的前几位。

(3) repeat
repeat方法用来返回重复多次的字符串。

let str = "abc";
console.log(str.repeat(2)); // "abcabc"
console.log(str.repeat("3")); // "abcabc"
console.log(str.repeat("2.8")); // "abcabc"
console.log(str.repeat("a")); // ""
console.log(str.repeat()); // ""
console.log(str.repeat(0)); // ""
console.log(str.repeat(-1)); // 报错: Uncaught RangeError: Invalid count value

repeat参数为重复字符串的次数,如果参数是字符串,则会先转换成数字,否则输出空字符串;如果是小数,则会被去整;如果参数是0或者不添加参数,则返回空字符串;如果是负数,则会报错。


4.解构赋值

解构赋值可以从数组或对象中提取出值为赋值给不同的变量。
(1)数组的解构赋值
ES6之前:

var arr = [1,2,3];
var a = arr[0];
var b = arr[1];
var c = arr[2];

ES6:

let [a,b,c] = [1,2,3];
console.log(a,b,c); //1 2 3

数组的解构赋值按照数据的变量顺序进行赋值。

(2)对象的解构赋值
ES6之前:

var obj = {name : "dudu",age : 20,city : "shenzhen"};
var name = obj.name;
var age = obj.age;
var city = obj.city;

ES6:

let {name,age,city} = {name : "dudu",age : 20,city : "shenzhen"};
console.log(name,age,city);

对象的解构赋值顺序可以打乱,按照变量名进行赋值。

(3)字符串的解构赋值
ES6之前:

var str = "hello";
var a = str[0];
var b = str[1];
var c = str[2];
var d = str[3];
var e = str[4];

ES6:

let [a,b,c,d,e] = 'hello';
console.log(a,b,c,d,e); //h e l l o

(4)函数参数的解构赋值
函数参数的解构赋值最大的特点是能够为参数设定默认值。
ES6之前:

function foo(name,age){
name = name || "dudu";
age = age || 20;
console.log(name,age);
}
foo(); //dudu 20
foo("haha",21) //haha 21

ES6:

function foo({name="dudu",age=20} = {}){
console.log([name,age]);
}
foo(); //["dudu", 20]
foo({}) //["dudu", 20]
foo({name : "cici"}) //["cici", 20]
foo({age : 22}); //["dudu", 22]
foo({name : "cici",age :22});//["cici", 22]

注意下面代码参数的写法与上面代码参数的写法不同:

function foo({name,age} = {name : "dudu",age : 20}){
console.log([name,age]);
}
foo(); //["dudu", 20]
foo({}); //[undefined, undefined]
foo({name : "cici"}) //["cici", undefined]
foo({age : 22}); //[undefined, 22]
foo({name : "cici",age :22}); //["cici", 22]

第一个代码块中,是为参数中对象的每一个名赋值,第二个代码块中是直接为对象赋值。


5.函数参数

ES6在函数参数上新增加了默认参数、不定参数等。
(1)默认参数
ES6之前:

function foo(x,y){
x = x || 1;
y = y || 2;
console.log(x,y)
}
foo(); //1 2

ES6:

function foo(x = 1,y = 2){
console.log(x,y)
}
foo(); //1 2

(2) 不定参数
ES6中函数的不定参数通过(…变量名)实现,用来获取函数多余的参数。

function add(...value){
let sum = 0;
for(var val of value){
sum += val;
}
console.log(sum);
}
add(1,2,3,4,5,6);

不定参数与arguments的区别是,不定参数中的变量代表的是一个数组,可以使用数组的所有方法,而arguments是类数组,只能用length属性。


6.类 class

ES6为了更接近传统语言的写法,提出了类的写法,作为对象的模版,通过关键字class来定义类。
ES6之前:

function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
Person.prototype.getInfo = function(){
console.log(this.name,this.age,this.sex)
}

ES6:

class Person{
constructor(name,age,sex){
this.name = name;
this.age = age ;
this.sex = sex;
}
getInfo(){
console.log(this.name,this.age,this.sex)
}
}

在上面的代码中,constructor方法就是构造方法。使用的时候直接用new命令,自动调用constructor方法。如果没有显示定义constructor方法,则会自动添加空的constructor

let person1 = new Person("dudu",20,"women");
person1.getInfo(); //dudu 20 women

类通过extends关键字进行继承。

class student extends Person{
constructor(name,age,sex,school){
super(name,age,school);
this.school = school;
}
getInfo(){
super.getInfo();
console.log(this.school);
}
}

super关键字是用来继承新建父类的this对象,因为子类没有自己的this对象,新建实例的时候会报错。使用super之后,可继承父类的this对象。
ES6提出的类实现继承,比ES5通过原型链实现继承要清晰,可以让我们写出更加简洁的代码。


7.Symbols

symbols是ES6新增的第七种原始数据类型,表示独一无二的值。简单来说,就是用来做标记的方法。
用法:

let a = Symbol();
console.log(typeof a) //symbol

上面代码中,通过typeof得出asymbol数据类型。
symbol创造出来的符号是独一无二的,因此设置了相同参数symbol函数的返回值是不想等的。

console.log(Symbol() === Symbol());                     //false
console.log(Symbol("a") === Symbol("a")) //false

symbol可以用于对象的属性名,由于每个symbol值都是不相等的,所以就可以用来作为标记,就可以保证不会出现相同的属性。

let a = Symbol();
let obj = {};
obj[a] = "hello";
console.log(obj[a]); // hello

有时候会需要重新使用同一个symbol值,可以使用 symbol.for方法。这个方法会接受一个字符串作为参数,搜索有没有以该参数作为名称的symbol值,如果有,则返回symbol值,否则就新建并返回一个以该字符串为参数的symbol值。

console.log(Symbol.for("a") === Symbol.for("a"))

上面代码中,返回true,说明上面两个的symbol为同一个值。


8.for…of循环

for...of是ES6新增的遍历器。可以遍历数组、类数组对象、字符串、对象等。
ES6前:

let arr = ["a","b","c","d"];
for(var i in arr){
console.log(arr[i]) // a b c d
}

ES6:

let arr = ["a","b","c","d"];
for(let val of arr){
console.log(val) // a b c d
}

遍历数组可以直接获取数组值。
entries()、keys()、values()
entries()keys()values()用来遍历数组。
entries()是对键值对的遍历。
keys()是对键名的遍历。
values()是对键值的遍历。

let arr = ["a","b","c","d"];
for(let val of arr.keys()){
console.log(val); // 0,1,2,3
}
for(let val of arr.values()){
console.log(val); // a,b,c,d
}
for(let [i,val] of arr.entries()){
console.log(i,val); // 0 "a"
// 1 "b"
// 2 "c"
// 3 "d"
}


9.Set和Map数据解构

set和WeakSet
ES6增添了新的数据结构集set和弱集weakset,类似于数组,但是setweakset具有元素的唯一性,若添加了已存在的元素,会被自动忽略。

let set = new Set();
set.add("hello").add("world").add("hello");
console.log(set.size); // 2
console.log(set); // Set {"hello", "world"}

由于具有元素的唯一性,所以可以利用set来除去数组重复的元素。

let arr = [2,3,4,2,5,3,3,3,2];
arr = Array.from(new Set(arr));
console.log(arr); //[2, 3, 4, 5]

set的属性:
size:用来获取set实例的元素个数。
set的方法有四个:
add:添加实例元素。
delete:删除某个元素。
has:判断某个元素是否存在。
clear:清除所有元素。

let set = new Set();
set.add("hello").add("world").add("hello");
console.log(set.size); // 2
console.log(set); // Set {"hello", "world"}

console.log(set.has("world")); // ture
set.delete("world");
console.log(set.has("world")); //false
set.clear()
console.log(set); //Set {}

weakset是弱集,与set相比,它能够检查元素的变量引用情况,如果元素的引用已被全部解除,则该元素就会删除,可以节省空间内存。另外,weakset的成员只能是对象。

let weakset = new WeakSet();
weakset.add("hello"); // TypeError: Invalid value used in weak set

let str = new String("hello");
weakset.add(str);
console.log(weakset.has(str)); //true

map和weakmap
mapweakmap则与原本的object相似,都是key/value的键值对结构,但是objectkey值必须是字符串或数字,而map可以使用任何对象作为key值。

let map = new Map();
let obj = { name : "dudu"};

map.set(obj,'hello');
map.set('hello','world');
console.log(map); //Map {Object {name: "dudu"} => "hello", "hello" => "world"}
console.log(map.size); //2
console.log(map.has(obj)); // true
console.log(map.get("hello")); // world

set的属性和方法:
size:用来获取map实例的元素个数。
set:用来设置key对应的键值。
get:用来获取key对应的键值。
delete:删除某个键。
has:判断某个键是否存在。
clear:清除所有键。
weakmapweakset相似,但是weakmap会检查键和值,只要其中一个的引用全被解除,则该键值对就会被删除。

let map = new Map();
let obj = { name : "dudu"};

map.set(obj,'hello');
map.set('hello','world');
console.log(map.has(obj)); //true
obj = null;
console.log(map.has(obj)); //false


10.promise

ES6新添的promise是可以用来解决回调函数无限嵌套的工具,也就是可以获取异步操作的消息,进行相应的处理。
promise的状态变化有两种:从pending未完成到resolved成功和pending未完成到rejected失败,一旦这两个状态任一发生了,就会进行下一步操作。
用法:
(1)创建一个promise实例,参数为一个函数,向该函数的传入两个参数分别为resolvereject

var promise = new Promise(function(resolve,reject){
if(/*成功*/){
resolve(value);
}else{
reject(error);
}
})

resolve函数是当promise对象的状态从pending未完成到resolved成功时,进行的异步操作。
reject函数是当promise对象的状态从pending未完成到rejected失败时,进行的异步操作。
(2)当promise实例创建好之后,则用then方法分别指定resolvereject状态的毁掉函数。

promise.then(function(value){
//成功
},function(error){
//失败
})

then方法中,参入两个函数作为参数,第一个参数是promise对象的状态变成成功resolve时执行的回调函数。第二个参数是promise对象的状态报错reject时执行的回调函数。
也可以通过catch方法进行reject状态的回调函数。

promise.then(function(value){
//成功
}).catch(function(error){
//失败
})

promise对象的状态变成resolve时,则执行then方法,当promise对象的状态变成reject时,则执行catch方法。

异步加载图片实例:

function loadImageAsync(url){
return new Promise(function(resolve,reject){
var image = new Image();

image.onload = function(){
resolve(image);
};
image.onerror = function(){
reject(new Error('could not load image at ' + url));
};
image.src = url;
});
}

loadImageAsync('./images/1.png').then(function(image){
//success
}).catch(function(error){
//failure
})


11.Generators生成器

ES6提供的Generators函数是一种异步编程解决方案,本质上是一个可以暂停计算并且可以随后返回表达式的值的函数。与普通函数相比,Generators有两个不同点:1.定义Generators函数需要在function与函数名之间插入*;2、在函数内部,使用yield语句来切出返回值。
yieldreturn相似,但是yield不退出函数,只是在函数运行过程中,通过.next()切出一个值。

function* helloworld(){

}

斐波那契数列例子:
(斐波那契数列:从第三项开始,值为前两项之和,第一二项都为1)
(1)构建生成器

function* Fibo(){
let [a,b] = [1,1];
yield a;
yield b;

while(true){
[a,b] = [b,a+b];
yield b;
}
}

(2)启动生成器

let fibo = Fibo();

(3)运行生成器
输出前十项斐波那契数:

let arr = [];
for(let i = 0;i < 10;i++){
arr.push(fibo.next().value);
}
console.log(arr); //[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]