Sanakey

ES6常用新特性(二)
前言ES6增加了不少新特性,下面继续整理了一下比较常用的新特性。SetMapPromiseClassModuleS...
扫描右侧二维码阅读全文
02
2018/04

ES6常用新特性(二)

前言

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

  • Set
  • Map
  • Promise
  • Class
  • Module

Set

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

Set实例的增删改查方法

  • add(value):添加某个值,返回 Set 结构本身。
  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  • has(value):返回一个布尔值,表示该值是否为Set的成员。
  • clear():清除所有成员,没有返回值。
{
  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():使用回调函数遍历每个成员

{
  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
}

去除数组的重复成员

{
  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 更合适。
{
  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增删改查对比
{
  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的对比
{
  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对比
{
 
  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写法

//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)的别名,用于指定发生错误时的回调函数。
{
  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方法创建实例的回调函数。
{
  // 所有图片加载完再添加到页面
  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 实例,再进一步处理。
{
  // 有一个图片加载完就添加到页面
  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 的通过修改原型链实现继承,要清晰和方便很多。
{
  // 基本定义和生成实例
  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关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。


{
  // 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指的是类,而不是实例。

{
  // 静态方法
  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命令用于输入其他模块提供的功能。

// 需要导出的模块文件
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 入门

Last modification:November 3rd, 2019 at 03:57 pm
如果觉得我的文章对你有用,请随意赞赏

Leave a Comment

lamu.png