ES6常用新特性(二)

前言

ES6增加了不少新特性,下面继续整理了一下比较常用的新特性。

  • Set
  • Map
  • Promise
  • Class
  • Module

Set

  • Set类似于数组,但是成员的值都是唯一的,没有重复的值。

Set实例的增删改查方法

  • add(value):添加某个值,返回 Set 结构本身。
  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  • has(value):返回一个布尔值,表示该值是否为Set的成员。
  • clear():清除所有成员,没有返回值。
1
2
3
4
5
6
7
8
9
10
11
12
{
let arr=['add','delete','clear','has'];
let list=new Set(arr);

console.log('has',list.has('add')); //has true

console.log('delete',list.delete('add'),list);
// delete true Set(3) {"delete", "clear", "has"}
list.clear();
console.log('list',list);
//list Set(0){}
}

Set结构的实例有四个遍历方法,可以用于遍历成员。

keys():返回键名的遍历器
values():返回键值的遍历器
entries():返回键值对的遍历器
forEach():使用回调函数遍历每个成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{
let arr=['add','delete','clear','has'];
let list=new Set(arr);

for(let key of list.keys()){
console.log('keys',key);
// keys add
// keys delete
// keys clear
// keys has
}
for(let value of list.values()){
console.log('value',value);
//value add
//value delete
//value clear
//value has

}
for(let [key,value] of list.entries()){
console.log('entries',key,value);
//entries add add
//entries delete delete
//entries clear clear
//entries has has

}

list.forEach(function(item){console.log(item);})
//add delete clear has
}

去除数组的重复成员

1
2
3
4
5
6
7
{
let arr = [1,2,3,3,5,4,4,5];
let list = new Set(arr);

console.log(list);
//Set(5) {1, 2, 3, 5, 4}
}

Map

  • JavaScript 的对象(Object),本质上是键值对的集合(Hash结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。
  • ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
  • Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应
  • 如果你需要“键值对”的数据结构,Map 比 Object 更合适。
1
2
3
4
5
6
7
8
9
{
let map = new Map();
let arr=['123'];

map.set(arr,456);

console.log('map',map,map.get(arr));
//map Map(1) {Array(1) => 456} 456
}

Map 结构的实例有以下属性和操作方法。

  • size属性返回 Map 结构的成员总数。
  • set方法设置键名key对应的键值为value,然后返回整个 Map结构。如果key已经有值,则键值会被更新,否则就新生成该键。set方法返回的是当前的Map对象,因此可以采用链式写法。
  • get方法读取key对应的键值,如果找不到key,返回undefined。
  • has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。
  • delete方法删除某个键,返回true。如果删除失败,返回false。
  • clear方法清除所有成员,没有返回值。

Map的遍历方法同Set

Map Set Array Object数据结构对比

  • map与array增删改查对比
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
let map=new Map();
let array=[];
// 增
map.set('t',1);
array.push({t:1});

console.info('map-array',map,array);

// 查
let map_exist=map.has('t');
let array_exist=array.find(item=>item.t);
console.info('map-array',map_exist,array_exist);

// 改
map.set('t',2);
array.forEach(item=>item.t?item.t=2:'');
console.info('map-array-modify',map,array);

// 删
map.delete('t');
let index=array.findIndex(item=>item.t);
array.splice(index,1);
console.info('map-array-empty',map,array);
}
  • set和array的对比
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
let set=new Set();
let array=[];

// 增
set.add({t:1});
array.push({t:1});

console.info('set-array',set,array);

// 查
let set_exist=set.has({t:1});
let array_exist=array.find(item=>item.t);
console.info('set-array',set_exist,array_exist);

// 改
set.forEach(item=>item.t?item.t=2:'');
array.forEach(item=>item.t?item.t=2:'');
console.info('set-array-modify',set,array);

// 删
set.forEach(item=>item.t?set.delete(item):'');
let index=array.findIndex(item=>item.t);
array.splice(index,1);
console.info('set-array-empty',set,array);
}
  • map,set,object对比
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{

let item={t:1};
let map=new Map();
let set=new Set();
let obj={};

// 增
map.set('t',1);
set.add(item);
obj['t']=1;

console.info('map-set-obj',obj,map,set);

// 查
console.info({
map_exist:map.has('t'),
set_exist:set.has(item),
obj_exist:'t' in obj
})

// 改
map.set('t',2);
item.t=2;
obj['t']=2;
console.info('map-set-obj-modify',obj,map,set);

// 删除
map.delete('t');
set.delete(item);
delete obj['t'];
console.info('map-set-obj-empty',obj,map,set);
}

建议性总结

  • 能使用map,就不使用数组和object,如需保证数据的唯一性,考虑使用set。

Promise

Promise 是异步编程的一种解决方案

  • 对象的状态不受外界影响。Promise对象的三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
  • 一旦状态改变,就不会再变,任何时候都可以得到这个结果。
  • Promise 实例具有then方法, then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。

ES5异步写法与ES6的Promise写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//ES5写法
{
// 基本定义
let ajax=function(callback){
console.log('ES5执行');
setTimeout(function () {
callback&&callback.call()
}, 1000);
};
ajax(function(){
console.log('ES5:timeout');
})
}

//ES6写法
{
let ajax=function(){
console.log('执行2');
return new Promise(function(resolve,reject){
setTimeout(function () {
resolve()
}, 1000);
})
};

ajax().then(function(){
console.log('promise','timeout2');
})
}

//ES6多个异步
{
let ajax=function(){
console.log('执行3');
return new Promise(function(resolve,reject){
setTimeout(function () {
resolve()
}, 1000);
})
};

ajax()
.then(function(){
return new Promise(function(resolve,reject){
setTimeout(function () {
resolve()
}, 2000);
});
})
.then(function(){
console.log('timeout3');
})
}
  • Promise 新建后立即执行,然后then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行。
  • 从上面的例子可以看出,相对于ES5而言,ES6的Promise避免了回调地狱,并且代码阅读性很高,后期维护可以很轻松看清楚异步函数的先后顺序。

catch

  • Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
let ajax=function(num){
console.log('执行4');
return new Promise(function(resolve,reject){
if(num>5){
resolve()
}else{
throw new Error('出错了')
}
})
}

ajax(6).then(function(){
console.log('log',6);
}).catch(function(err){
console.log('catch',err);
});

ajax(3).then(function(){
console.log('log',3);
}).catch(function(err){
console.log('catch',err); //catch Error: 出错了
});
}

all

  • Promise.all方法创建的实例只有在接收的参数状态全部为fulfilled时, Promise.all方法包装的实例才会变为fulfilled, 或者之中有一个被rejected,该实例才会变为rejected。此时第一个被reject的实例的返回值,会传递给Promise.all方法创建实例的回调函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
// 所有图片加载完再添加到页面
function loadImg(src){
return new Promise((resolve,reject)=>{
let img=document.createElement('img');
img.src=src;
img.onload=function(){
resolve(img);
}
img.onerror=function(err){
reject(err);
}
})
}

function showImgs(imgs){
imgs.forEach(function(img){
document.body.appendChild(img);
})
}

Promise.all([
loadImg('2.png'),
loadImg('ba.png'),
loadImg('c.png')
]).then(showImgs)

//只有当3个实例的状态都变成fulfilled,或者其中有一个变为rejected,才会调用Promise.all方法后面的回调函数。
}

race

  • Promise.race方法的参数与Promise.all方法一样,如果不是 Promise 实例,就会先调用Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
// 有一个图片加载完就添加到页面
function loadImg(src){
return new Promise((resolve,reject)=>{
let img=document.createElement('img');
img.src=src;
img.onload=function(){
resolve(img);
}
img.onerror=function(err){
reject(err);
}
})
}

function showImgs(img){
let p=document.createElement('p');
p.appendChild(img);
document.body.appendChild(p)
}

Promise.race([
loadImg('32.png'),
loadImg('a.png'),
loadImg('bc.png')
]).then(showImgs)

}

Class

  • ES6 的class可以让对象原型的写法更加清晰、更像面向对象编程的语法。
  • Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
{
// 基本定义和生成实例
class Parent{
constructor(name='wang'){
this.name=name;
}
}
let v_parent=new Parent('v');
console.log('构造函数和实例',v_parent); //构造函数和实例 Parent {name: "v"}
}

{
// 继承
class Parent{
constructor(name='wang'){
this.name=name;
}
}

class Child extends Parent{

}

console.log('继承',new Child()); //继承 Child {name: "wang"}
}

{
// 继承传递参数
class Parent{
constructor(name='wang'){
this.name=name;
}
}

class Child extends Parent{
constructor(name='child'){
this.color = "color"; // ReferenceError
super(name); //super必须在最前面调用
this.type='child'; //正确
}
}

console.log('继承传递参数',new Child('hello'));
// 继承传递参数 Child {name: "hello", type: "child"}
}

ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this

与 ES5 一样,在“类”的内部可以使用getset关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

{
// getter,setter
class Parent{
constructor(name='wang'){
this.name=name;
}

get longName(){
return 'mk'+this.name
}

set longName(value){
this.name=value;
}
}

let v=new Parent();
console.log('getter',v.longName); //getter mkwang
v.longName='hello';
console.log('setter',v.longName); // setter mkhello
}

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

如果静态方法包含this关键字,这个this指的是类,而不是实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
// 静态方法
class Parent{
constructor(name='wang'){
this.name=name;
}

static tell(){
console.log('tell'); //tell
}
}

Parent.tell();

}

{
// 静态属性
class Parent{
constructor(name='wang'){
this.name=name;
}

static tell(){
console.log('tell'); //tell
}
}

Parent.type='test';

console.log('静态属性',Parent.type); //静态属性 test

}

Module模块化

模块功能主要由两个命令构成:exportimportexport命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 需要导出的模块文件
let A=123;
let test=function(){
console.log('test');
}
class Hello{
test(){
console.log('class');
}
}

export default {
A,
test,
Hello
}

//在其他文件中引入导出的模块
import Module from '../Module.js';

参考

阮一峰 ECMAScript 6 入门


感谢打赏,错误之处欢迎指正交流(`・ω・´) !~~



文章目录
  1. 1. 前言
  2. 2. Set
    1. 2.1. Set实例的增删改查方法
    2. 2.2. Set结构的实例有四个遍历方法,可以用于遍历成员。
  3. 3. Map
    1. 3.1. Map 结构的实例有以下属性和操作方法。
    2. 3.2. Map Set Array Object数据结构对比
  4. 4. Promise
    1. 4.1. catch
    2. 4.2. all
    3. 4.3. race
  5. 5. Class
  6. 6. Module模块化
  7. 7. 参考
|