ECMAScript6(day04)
ECMAScript6(day04)
1、Set 和 WeakSet用法
1.1什么是set
Set是ES6给开发者带来的一种新的数据结构,你可以理解为值的集合。我们平时见到的数组Array也是一种数据结构,但是Set跟其他数据结构不同的地方就在于:它的值不会有重复项。
1.2set的基本用法
一、
var s = new Set();
console.log(s);
//打印结果:Set {}
二、
var s = new Set([1,2,3]);
console.log(s);
//打印结果:Set {1, 2, 3}
三、
var s = new Set();
//使用add方法添加成员
s.add(1);
s.add(2);
s.add(3);
console.log(s);
//打印结果:Set {1, 2, 3}
1.3成员值唯一
不过,为Set 结构添加成员值的时候,要注意一点是,set结构的成员值是没有重复的,每个值都是唯一的
var s = new Set();
s.add(1);
s.add(1);
console.log(s);
//打印结果: Set {1}
1.1 size属性
size属性:获取成员的个数。
let s = new Set([1,2,3,4]);
s.size; //结果:4
1.5delete属性
delete( )方法:用户删除Set结构中的指定值,删除成功返回:true,删除失败返回:fasle。
//用数组作为参数初始化
var s = new Set([1,2,3]);
console.log(s);
//打印结果:Set {1, 2, 3}
//使用delete方法删除指定值
s.delete(2);//结果:true
s.delete(4);//结果:false
console.log(s);
//打印结果:Set {1, 3}
1.6clear方法
clear( )方法:清除所有成员。
//用数组作为参数初始化
var s = new Set([1,2,3]);
console.log(s);
//打印结果:Set {1, 2, 3}
s.clear();
console.log(s);
//打印结果:Set {}
1.7has方法
has( )方法:判断set结构中是否含有指定的值。如果有,返回true;如果没有,返回fasle。
//用数组作为参数初始化
var s = new Set([1,2,3]);
s.has(1);//结果:true
s.has(4);//结果:false
当判断变量s中是否含有数字1时,得到的结果是true,判断是否含有数字4的时候,得到的结果是false。
1.8enteries方法
entries( )方法:返回一个键值对的遍历器。
//用数组作为参数初始化
var s = new Set(['a','b','c']);
s.entries();
//结果:SetIterator {["a", "a"], ["b", "b"], ["c", "c"]}
注意得到的结果,成员值“a”对应的键值对是[“a”,”a”],也就是说:Set结构是键名和键值是同一个值。
1.9keys和values方法
keys( )方法:返回键名的遍历器。
values( )方法:返回键值的遍历器。
//用数组作为参数初始化
var s = new Set(['a','b','c']);
s.keys();
//结果:SetIterator {"a", "b", "c"}
s.values();
//结果:SetIterator {"a", "b", "c"}
我把两个函数放在一起演示,是因为上面entries()方法的使用告诉我们,Set结构的键名和键值是同一个值,那么我们就用Set结构提供的keys( )和values()方法来检验一下。从得到的结果可以看出:两者确实一致。
//用数组作为参数初始化
var s = new Set(['a','b','c']);
//用for...of遍历
for(let [i,v] of s.entries()){
console.log(i+' '+v);
}
//打印结果:a a
// b b
// c c
1.10foreach方法
forEach( )方法:遍历每一个成员。
//用数组作为参数初始化
var s = new Set(['a','b','c']);
//使用回调函数遍历每个成员
s.forEach(function(value,key){
console.log(value,key)
});
//打印结果:a a
// b b
// c c
使用方式跟数组的forEach一样。当然,得到的value是key的值是一样的。
1.1 set用途之一
//目标数组arr,要求去重
let arr = [1,2,2,3,4,4,4];
let s = new Set(arr);
//结果:Set {1,2,3,4}
let newArr = Array.from(s);
//结果:[1,2,3,4],完成去重
上面的代码重点是一个含有重复元素的数组,作为参数,用于初始化一个Set实例对象,因为Set结构不会含有重复成员,就会自动忽略相同的元素,只保留一个相同的值。
1.12Weakset结构
讲到Set结构,就不能不讲WeakSet结构。Set和WeakSet两者名字就很像,注定它们有很多一样的地方。
WeakSet结构同样不会存储重复的值,不同的是,它的成员必须是对象类型的值。
//初始化一个WeakSet对象
let ws = new WeakSet([{"age":18}]);
//结果:WeakSet {Object {age: 18}}
我们初始化一个WeakSet对象,参数一个数组,数组的成员必须是对象类型的值{"age":18},否则就会报错。
//初始化一个WeakSet对象
let ws = new WeakSet([1,2]);
//结果:报错
以上的写法就会报错,因为数组的元素不是对象类型的,是数字1,2。
实际上,任何可遍历的对象,都可以作为WeakSet的初始化参数。比如:数组。
let arr1 = [1];
let arr2 = [2];
//初始化一个WeakSet对象,参数是数组类型
let ws = new WeakSet([arr1,arr2]);
//结果:WeakSet {Object {age: 18}}
同样,WeakSet结构也提供了add( ) 方法,delete( ) 方法,has( )方法给开发者使用,作用与用法跟Set结构完全一致。
2、Map和WeakMap用法
介绍什么是Map,就不得不说起Object对象,我们都知道Object对象是键值对的集合:
//Object对象
{"name":"前端君","gender":1}
Map的基本用法
Map结构提供了一个构造函数给我们,我们使用的时候需要用new来创建实例:
let m = new Map();
如果想要在创建实例的同时,初始化它的内容,我们可以给它传参,形式跟Set结构类型,都是需要用数组作为参数,我们来试试看看:
let m = new Map([
["name","前端君"],
["gender",1]
]);
console.log(m);
//打印结果:Map {"name" => "前端君", "gender" => 1}
set方法
set( )方法作用:给实例设置一对键值对,返回map实例。
let m = new Map();
//set方法添加
//添加一个string类型的键名
m.set("name","前端君");
//添加一个数字类型的键名
m.set(1,2);
console.log(m);
//打印结果:Map {"name" => "前端君", 1 => 2}
//数组类型的键名
m.set([1],2);
//对象类型的键名
m.set({"name":"Zhangsan"},2);
//布尔类型的键名
m.set(true,2);
//Symbol类型的键名
m.set(Symbol('name'),2);
//null为键名
m.set(null,2);
//undefined为键名
m.set(undefined,2);
let m = new Map();
m.set("name","前端君");
console.log(m);
//结果:Map {"name" => "前端君"}
//再次设置name的值
m.set("name","隔壁老王");
console.log(m);
//结果:Map {"name" => "隔壁老王"}
get方法
get( )方法作用:获取指定键名的键值,返回键值。
let m = new Map([["name","前端君"]]);
m.get("name");//结果:前端君
m.get("gender");//结果:undefined
get方法使用也很简单,只需要指定键名即可。获取存在对应的键值,如果键值对存在,就会返回键值;否则,返回undefined,这个也很好理解。
delete方法
delete( )方法作用:删除指定的键值对,删除成功返回:true,否则返回:false。
let m = new Map();
m.set("name","前端君");
//结果:Map {"name" => "前端君"}
m.delete("name");//结果:true
m.delete("gender");//结果:false
我们使用delete方法,删除“name”的时候成功,返回了true。删除“gender”的时候,由于Map结构中不存在键名:“gender”,所以删除失败,返回false。
clear方法
跟Set结构一样,Map结构也提供了clear( )方法,让你一次性删除所有键值对。
let m = new Map();
m.set("name","前端君");
m.set("gender",1);
m.clear();
console.log(m);
//打印结果:Map {}
使用clear方法后,我们再打印一下变量m,发现什么都没有,一个空的Map结构,说明clear方法起作用了。
has方法
has( )方法作用:判断Map实例内是否含有指定的键值对,有就返回:true,否则返回:false。
let m = new Map();
m.set("name","前端君");
m.has('name');//结果:true
m.has('age');//结果:false
Map实例中含有键名:name,就返回了true,键名age不存在,就返回false。好理解吧,比较简单。
entries方法
entries( )方法作用:返回实例的键值对遍历器。
let m = new Map([
["name","前端君"],
["age",25]
]);
for(let [key,value] of m.entries()){
console.log(key+' '+value);
}
//打印结果:name 前端君
// age 25
案例中的 m.entries( ) 返回键值对的遍历器,使用了for...of来遍历这个遍历器,得到的值分别赋值到key和value,然后控制台分别输出它们。
keys和values方法
keys( )方法:返回实例所有键名的遍历器。
values( ) 方法:返回实例所有键值的遍历器。
既然都是遍历器,那就用for...of把它们遍历出来吧:
let m = new Map([
["name","前端君"],
["age",25]
]);
//使用keys方法获取键名
for(let key of m.keys()){
console.log(key);
}
//打印结果:name
//age
//使用values方法获取键值
for(let value of m.values()){
console.log(value);
}
//打印结果:前端君
// 25
keys方法和values方法的使用方式一致,只是返回的结果不同。
forEach方法
除了使用以上三个方法实现遍历以外,我们还可以使用forEach遍历每一个键值对:
let m = new Map([
["name","前端君"],
["age",25]
]);
m.forEach(function(value,key){
console.log(key+':'+value);
});
//打印结果:name:前端君
// age:25
forEach方法接收一个匿名函数,给匿名函数传参value和key,分别是Map实例的键名和键值,这个方法的使用相信大家一定不会陌生。
size属性
其中一个常用的属性就是size:获取实例的成员数。
let m = new Map();
m.set(1,3);
m.set('1','3');
m.size;//结果:2
WeakMap的基本用法
WeakMap结构的使用方式和Map结构一样:
let wm = new WeakMap();
两者都是使用new来创建实例。如果添加键值对的话,我们同样是使用set方法,不过键名的类型必须要求是引用类型的值,我们来看看:
let wm = new WeakMap();
//数组类型的键名
wm.set([1],2);
//对象类型的键名
wm.set({'name':'Zhangsan'},2);
//函数类型的键名
function fn(){};
wm.set(fn,2);
console.log(wm);
//打印:WeakMap {
[1] => 2,
Object {name: "Zhangsan"} => 2,
function => 2
}
从打印结果可以看出,以上类型的键名都可以成功添加到WeakMap实例中。
WeakMap与Map的区别?
1.如果是普通的值类型则不允许。比如:字符串,数字,null,undefined,布尔类型。而Map结构是允许的,这就是两者的不同之处,谨记。
2.跟Map一样,WeakMap也拥有get、has、delete方法,用法和用途都一样。不同地方在于,WeakMap不支持clear方法,不支持遍历,也就没有了keys、values、entries、forEach这4个方法,也没有属性size。
3.理由跟WeakSet结构一样:键名中的引用类型是弱引用,你永远不知道这个引用对象什么时候会被垃圾回收机制回收了,如果这个引用类型的值被垃圾机制回收了,WeakMap实例中的对应键值对也会消失。
3、ES6的Promise对象
Promise设计初衷
你需要用ajax进行多次请求,而且,每次请求都依赖上一次请求返回的数据来作为参数,然后继续发出请求,你把代码写成了这样:
//------请求A 开始---------
$.ajax({
success:function(res1){
//------请求B 开始----
$.ajax({
success:function(res2){
//----请求C 开始---
$.ajax({
success:function(res3){
}
});
//---请求C 结束---
}
});
//------请求B 结束-----
}
});
//------请求A 结束---------
传统写法的缺点:
1. 如果存在多个请求操作层层依赖的话,那么以上的嵌套就有可能不止三层那么少了,加上每一层还会有复杂的业务逻辑处理,代码可读性也越来越差,不直观,调试起来也不方便。如果多人开发的时候没有足够的沟通协商,大家的代码风格不一致的话,更是雪上加霜,给后面的维护带来极大的不便。
2. 如果请求C的需要依赖A和B的结果,但是请求A和B缺互相独立,没有依赖关系,以上的实现方式,就使得B必须得到A请求完成后才可以执行,无疑是消耗了更多的等待时间。
既然使用这种回调函数层层嵌套(又称:回调地狱)的形式存在缺点,ES6想了办法治它,所以就有了Promise的出现了。
那么我们就知道了:Promise对象能使我们更合理、更规范地进行处理异步操作。
Promise基本用法
我们就看看它的基本用法:promise 承诺
Promise对象是全局对象,你也可以理解为一个类,创建Promise实例的时候,要有那个new关键字。参数是一个匿名函数,其中有两个参数:resolve(解决)和reject(拒绝),两个函数均为方法。resolve方法用于处理异步操作成功后业务;reject方法用于操作异步操作失败后的业务。
Promise的三种状态
Promise对象有三种状态:
1.pending:刚刚创建一个Promise实例的时候,表示初始状态;
2.fulfilled:resolve方法调用的时候,表示操作成功;
3.rejected:reject方法调用的时候,表示操作失败;
状态只能从 初始化 -> 成功 或者 初始化 -> 失败,不能逆向转换,也不能在成功fulfilled
和失败rejected之间转换。
let pro = new Promise(function(resolve,reject){
//实例化后状态:pending
if('操作成功'){
resolve();
//resolve方法调用,状态为:fulfilled
}else{
reject();
//reject方法调用,状态为:rejected
}
});
上面的注释,讲清楚了一个Promise实例的状态改变情况。
初始化实例后,对象的状态就变成了pending;当resolve方法被调用的时候,状态就变成了:成功fulfilled;当reject方法被调用的时候,状态就会有pending变成失败rejected。
Then方法
了解了Promise的创建和状态,我们来学习一个最重要的实例方法:then( )方法。
then( )方法:用于绑定处理操作后的处理程序。
pro.then(function (res) {
//操作成功的处理程序
},function (error) {
//操作失败的处理程序
});
参数是两个函数,第一个用于处理操作成功后的业务,第二个用于处理操作异常后的业务。
Catch方法
对于操作异常的程序,Promise专门提供了一个实例方法来处理:catch( )方法。
pro.catch(function (error) {
//操作失败的处理程序
});
catch只接受一个参数,用于处理操作异常后的业务。
综合上面的两个方法,大家都建议将then方法用于处理操作成功,catch方法用于处理操作异常,也就是:
pro.then(function (res) {
//操作成功的处理程序
}).catch(function (error) {
//操作失败的处理程序
});
之所以能够使用链式调用,是因为then方法和catch方法调用后,都会返回promise对象。
讲了那么多,如果你之前一点都没接触过Promise的话,现在一定很懵逼,没关系,下面我们用一个案例来串联前面的知识点,演示一下,认真阅读注释:
//用new关键字创建一个Promise实例
let pro = new Promise(function(resolve,reject){
//假设condition的值为true
let condition = true;
if(condition){
//调用操作成功方法
resolve('操作成功');
//状态:pending->fulfilled
}else{
//调用操作异常方法
reject('操作异常');
//状态:pending->rejected
}
});
//用then处理操作成功,catch处理操作异常
pro.then(function (res) {
//操作成功的处理程序
console.log(res)
}).catch(function (error) {
//操作失败的处理程序
console.log(error)
});
//控制台输出:操作成功
Promise.all方法
Promise.all( )方法:接受一个数组作为参数,数组的元素是Promise实例对象,当参数中的实例对象的状态都为fulfilled时,Promise.all( )才会有返回。
//创建实例pro1
let pro1 = new Promise(function(resolve){
setTimeout(function () {
resolve('实例1操作成功');
},5000);
});
//创建实例pro2
let pro2 = new Promise(function(resolve){
setTimeout(function () {
resolve('实例2操作成功');
},1000);
});
Promise.all([pro1,pro2]).then(function(result){
console.log(result);
});
//打印结果:["实例1操作成功", "实例2操作成功"]
我们执行某个操作,这个操作需要得到需要多个接口请求回来的数据来支持,但是这些接口请求之前互不依赖,不需要层层嵌套。这种情况下就适合使用Promise.all( )方法,因为它会得到所有接口都请求成功了,才会进行操作。
Promise.race方法
Promise.race()方法:它的参数要求跟Promise.all( )方法一样,不同的是,它参数中的promise实例,只要有一个状态发生变化(不管是成功fulfilled还是异常rejected),它就会有返回,其他实例中再发生变化,它也不管了。
//初始化实例pro1
let pro1 = new Promise(function(resolve){
setTimeout(function () {
resolve('实例1操作成功');
},4000);
});
//初始化实例pro2
let pro2 = new Promise(function(resolve,reject){
setTimeout(function () {
reject('实例2操作失败');
},2000);
});
Promise.race([pro2,pro1]).then(function(result){
console.log(result);
}).catch(function(error){
console.log(error);
});
//打印结果:实例2操作失败
4、类基本用法
4.1类的属性和方法
声明一个类的写法:

我们通过关键字class来声明一个名字叫Animal的类,可以看到类里面(花括号 {}里面)有一个叫constructor(构造、建造)方法,它就是构造方法,构造方法里面的this,指向的是该类实例化后的对象,这就是实现了一个类的声明。
4.2类的实例化对象
给类添加属性和方法。

上面的案例中,类体内有2个方法:constructor( )、getName()。
其中constructor方法是构造方法,在实例化一个类的时候被调用。constructor方法是必须的,也是唯一的,一个类体不能含有多个constructor构造方法。我们可以在方法里面自定义一些对象的属性,比如案例中的name属性。
此外,我们还自定义了一个getName( )方法,它属于类的实例方法,实例化后对象可以调用此方法。
4.3类的静态方法
如何创建对象和使用对象的实例方法:

还是同一个类Animal,我们通过new来创建了实例对象dog,构造方法会把传进去的参数“dog”通过this.name赋值给对象的name属性,所以dog的name属性为“dog”,对象dog还可以调用自己的实例方法getName( ),结果返回:“This is a dog”。
实例对象的创建有几个要注意的事项:
1.必须使用new创建字来创建类的实例对象
2.先声明定义类,再创建实例,否则会报错
4.4类的继承
ES6使用extends关键字来实现子类继承父类

定义两个类,Animal类作为父类,Dog类作为子类,然后通过关键字extends来实现继承,此外,我们还注意到一个关键字super,它相当于是父类中的this。
可以用super来引用父类


在父类中,我们定义了say方法,想要在子类中调用父类的say方法的话,我们使用super.say( )即可实现。
使用super有几个要注意的事项:
1.子类必须在constructor方法中调用super方法
2.调用super( ),才可以使用this,否则报错
5、Module模块
5.1模块化的初衷
目前还没有浏览器支持ES6的module模块。
假设现在有两个js文件,分别是module-A.js和module-B.js,我们把它们视为两个模块。带着这个假设,下面我们来学习module模块的几个概念以及它们的含义。
5.2模块Module
模块Module:一个模块,就是一个对其他模块暴露自己的属性或者方法的文件。
在这里,我们会把module-A.js和module-B.js分别当作两个模块(moduleA模块和moduleB模块)来对待和处理。用这两个模块来演示如何暴露一个模块的属性或方法。
5.3导出Export
导出Export:作为一个模块,它可以选择性地给其他模块暴露(提供)自己的属性和方法,供其他模块使用。
5.4导入Import
导入Import:作为一个模块,可以根据需要,引入其他模块的提供的属性或者方法,供自己模块使用。
5.5模块化的实现
带着这三个概念,我们来演示一下它们的基本用法:
moduleB模块代码:

moduleA模块代码:

5.6批量导出
对于模块B,如果你想导出(暴露)多个属性和方法的话,你可以这样实现:


5.7重命名导出的变量

5.8整体导入

5.9默认导出


5.10注意事项
1.声明的变量,对外都是只读的。

2.导入不存在的变量,值为undefined。

我们通过关键字class来声明一个名字叫Animal的类,可以看到类里面(花括号 {}里面)有一个叫constructor(构造、建造)方法,它就是构造方法,构造方法里面的this,指向的是该类实例化后的对象,这就是实现了一个类的声明。
浙公网安备 33010602011771号