set 、map 与模块化

1.Set 和 WeakSet用法

(1)、什么是set

Set是ES6给开发者带来的一种新的数据结构,你可以理解为值的集合。我们平时见到的数组Array也是一种数据结构,但是Set跟其他数据结构不同的地方就在于:它的值不会有重复项。

(2)、set的基本用法

Set的用法:

var s  = new Set()
console.log(s)
//打印结果:Set{}

Set本身是一个构造函数,你可以理解为一个类,使用的时候需要用new来创建一个实例。以上的案例就是这样创建出一个Set结构,我们打印出来看看,控制台输出:Set{ }。

(3)、成员值唯一

不过,为Set 结构添加成员值的时候,要注意一点是,set结构的成员值是没有重复的,每个值都是唯一的.

如果给它认为到添加相同的值,set会自动忽略相同的值。

(4)、size属性

size属性:获取成员的个数。

 let s = new Set([1,2,3,4]);
    s.size; //结果:4

(5)、delete属性

 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}

(6)、clear方法

clear( )方法:清除所有成员。

//用数组作为参数初始化
    var s = new Set([1,2,3]);
    console.log(s);
    //打印结果:Set {1, 2, 3}

    s.clear();
    console.log(s);
    //打印结果:Set {}

    上面的代码中,在使用clear( )方法之前和之后都打印了变量s的值,我们看到,clear( )方法使用之后,Set结构的成员都被清除了,一个不留。

(7)、has方法

has( )方法:判断set结构中是否含有指定的值。如果有,返回true;如果没有,返回fasle。

 //用数组作为参数初始化
    var s = new Set([1,2,3]);
    s.has(1);//结果:true
    s.has(4);//结果:false

当判断变量s中是否含有数字1时,得到的结果是true,判断是否含有数字4的时候,得到的结果是false。

(8)、enteries方法

entries( )方法:返回一个键值对的遍历器。

 //用数组作为参数初始化
    var s = new Set(['a','b','c']);
    s.entries();
    //结果:SetIterator {["a", "a"], ["b", "b"], ["c", "c"]}

注意得到的结果,成员值“a”对应的键值对是[“a”,”a”],也就是说:Set结构是键名和键值是同一个值。

(9)、keys和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()方法来检验一下。从得到的结果可以看出:两者确实一致。

(10)、foreach方法

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的值是一样的。

(11)、set用途

利用Set结构的成员值不能重复的特点,可以去重。

(12)、Weakset结构

讲到Set结构,就不能不讲WeakSet结构。Set和WeakSet两者名字就很像,注定它们有很多一样的地方。

        WeakSet结构同样不会存储重复的值,不同的是,它的成员必须是对象类型的值。

 //初始化一个WeakSet对象

    let ws = new WeakSet([{"age":18}]);
    //结果:WeakSet {Object {age: 18}}

     我们初始化一个WeakSet对象,参数一个数组,数组的成员必须是对象类型的值{"age":18},否则就会报错。

 //初始化一个WeakSet对象

    let ws = new WeakSet([{"age":18}]);
    //结果:WeakSet {Object {age: 18}}

      以上的写法就会报错,因为数组的元素不是对象类型的,是数字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结构完全一致。

        另一个不同点是:WeakSet 结构不可遍历。因为它的成员都是对象的弱引用,随时被回收机制回收,成员消失。所以WeakSet 结构不会有keys( ),values( ),entries( ),forEach( )等方法和size属性。

2.Map和WeakMap用法

(1)、什么是Map?

介绍什么是Map,就不得不说起Object对象,我们都知道Object对象是键值对的集合:

 //Object对象

    {"name":"前端君","gender":1}

ES5中的key键名的类型要求一定是字符串,当然,ES6已经允许属性名的类型是Symbol。在,ES6 提供了Map结构给我们使用,它跟Object对象很像,但是不同的是,它的key键名的类型不再局限于字符串类型了,它可以是各种类型的值;可以说,它比Object对象更加灵活了,当然,也更加复杂了。

(2)、Map的基本用法

Map结构提供了一个构造函数给我们,我们使用的时候需要用new来创建实例:

   let m = new Map();

 如果想要在创建实例的同时,初始化它的内容,我们可以给它传参,形式跟Set结构类型,都是需要用数组作为参数,我们来试试看看: 

let m = new Map([

            ["name","前端君"],
            ["gender",1]
    ]);
    console.log(m);
    //打印结果:Map {"name" => "前端君", "gender" => 1}

大家注意Map( )方法里面的参数,首先它是一个数组,而里面的内容也是由多个数组组成,“name”:“前端君”作为一个键值对,将它们装在一个数组里面,[“name”:“前端君”],另外一组键值对也一样:[“gender”:1 ]。这就是初始化一个Map结构实例的基本写法。

供我们实现对实例的操作。我们一起看看都有哪些属性和方法。

(3)、set方法

set( )方法作用:给实例设置一对键值对,返回map实例。

 let m = new Map();

//set方法添加
//添加一个string类型的键名
m.set("name","前端君");  
//添加一个数字类型的键名
m.set(1,2);
console.log(m);
//打印结果:Map {"name" => "前端君", 1 => 2}

set方法的使用很简单,只需要给方法传入key和value作为键名和键值即可。

注意:第三行代码中,我们传入的key是数字1,这就证明了,Map结构确实可以存储非字符串类型的键名,当然你还可以设置更多其它类型的键名,比如:

 //数组类型的键名

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);

  以上6种类型值都可以作为键名,可以成功添加键值对。

        使用set方法的时候有一点需要注意,如果你设置一个已经存在的键名,那么后面的键值会覆盖前面的键值。

(4)、get方法

get( )方法作用:获取指定键名的键值,返回键值。

let m = new Map([["name","前端君"]]);

m.get("name");//结果:前端君
m.get("gender");//结果:undefined

      get方法使用也很简单,只需要指定键名即可。获取存在对应的键值,如果键值对存在,就会返回键值;否则,返回undefined。

(5)、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。

(6)、clear方法

跟Set结构一样,Map结构也提供了clear( )方法,让你一次性删除所有键值对。

(7)、has方法

has( )方法作用:判断Map实例内是否含有指定的键值对,有就返回:true,否则返回:false。

(8)、entries方法

entries( )方法作用:返回实例的键值对遍历器。

(9)、keys和values方法

keys( )方法:返回实例所有键名的遍历器。

values( ) 方法:返回实例所有键值的遍历器。

keys方法和values方法的使用方式一致,只是返回的结果不同。

(10)、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实例的键名和键值。

(11)、size属性

其中一个常用的属性就是size:获取实例的成员数。

(12)、什么是WeakMap?

WeakMap结构和Map结构很类似,不同点在于WeakMap结构的键名只支持引用类型的数据。

(13)、WeakMap的基本用法

WeakMap结构的使用方式和Map结构一样:

 let wm = new WeakMap();

 两者都是使用new来创建实例。如果添加键值对的话,我们同样是使用set方法,不过键名的类型必须要求是引用类型的值。

(14)、WeakMap与Map的区别?

如果是普通的值类型则不允许。比如:字符串,数字,null,undefined,布尔类型。而Map结构是允许的,这就是两者的不同之处,谨记。

        跟Map一样,WeakMap也拥有get、has、delete方法,用法和用途都一样。不同地方在于,WeakMap不支持clear方法,不支持遍历,也就没有了keys、values、entries、forEach这4个方法,也没有属性size。

        理由跟WeakSet结构一样:键名中的引用类型是弱引用,你永远不知道这个引用对象什么时候会被垃圾回收机制回收了,如果这个引用类型的值被垃圾机制回收了,WeakMap实例中的对应键值对也会消失。

3.ES6的Promise对象

(1)、Promise设计初衷

解决"回调地狱"

(2)、Promise基本用法

我们就看看它的基本用法:promise 承诺

 Promise对象是全局对象,你也可以理解为一个类,创建Promise实例的时候,要有那个new关键字。参数是一个匿名函数,其中有两个参数:resolve(解决)和reject(拒绝),两个函数均为方法。resolve方法用于处理异步操作成功后业务;reject方法用于操作异步操作失败后的业务。

(3)、Promise的三种状态

Promise对象有三种状态:

    1.pending:刚刚创建一个Promise实例的时候,表示初始状态;

    2.fulfilled:resolve方法调用的时候,表示操作成功;

    3.rejected:reject方法调用的时候,表示操作失败;

状态只能从 初始化 -> 成功  或者  初始化 -> 失败,不能逆向转换,也不能在成功fulfilled 和失败rejected之间转换。

(4)、Then方法

了解了Promise的创建和状态,我们来学习一个最重要的实例方法:then( )方法。

  then( )方法:用于绑定处理操作后的处理程序。

  pro.then(function (res) {

        //操作成功的处理程序
    },function (error) {
        //操作失败的处理程序
    });

参数是两个函数,第一个用于处理操作成功后的业务,第二个用于处理操作异常后的业务。

(5)、Catch方法

对于操作异常的程序,Promise专门提供了一个实例方法来处理:catch( )方法。

 pro.catch(function (error) {

        //操作失败的处理程序
    });

 catch只接受一个参数,用于处理操作异常后的业务。

        综合上面的两个方法,大家都建议将then方法用于处理操作成功,catch方法用于处理操作异常,也就是:

pro.then(function (res) {

        //操作成功的处理程序
    }).catch(function (error) {
        //操作失败的处理程序
    });

之所以能够使用链式调用,是因为then方法和catch方法调用后,都会返回promise对象。

(6)、案例


    let pro = new Promise(function (resolve, reject) {
        if (true) { //调用操作成功方法         
            resolve('操作成功');
        } else {
            //调用操作异常方法           
            reject('操作异常');
        }
    });
    //用then处理操作成功,catch处理操作异常  
    pro.then(requestA).then(requestB).then(requestC).catch(requestError);

    function requestA() {
        console.log('请求A成功');
        return '请求B,下一个就是你了';
    }

    function requestB(res) {
        console.log('上一步的结果:' + res);
        console.log('请求B成功');
        return '请求C,下一个就是你了';
    }

    function requestC(res) {
        console.log('上一步的结果:' + res);
        console.log('请求C成功');
    }

    function requestError() {
        console.log('请求失败');
    }
    //打印结果:   
    //请求A成功 
    //上一步的结果:请求B,下一个就是你了  
    //请求B成功   
    //上一步的结果:请求C,下一个就是你了  
    //请求C成功

(7)、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操作成功"]

(8)、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操作失败

 同样是两个实例,实例pro1不变,不同的是实例pro2,这次我们调用的是失败函数reject。

        由于pro2实例中2000毫秒之后就执行reject方法,早于实例pro1的4000毫秒,所以最后输出的是:实例2操作失败

4.ES7中 的Async/await

(1)、为什么要有Async/await?

    Promise虽然跳出了异步嵌套的怪圈,解决了回调地狱的问题,用链式表达更加清晰,但是我们也发现如果有大量的异步请求的时候,流程复杂的情况下,会发现充满了屏幕的then,看起来非常吃力,而ES7的Async/Await的出现就是为了解决这种复杂的情况。

1.9.2 Async/await的基本使用

    这是ES7的语法,它是基于generator函数做的语法糖。async用于申明一个function是异步的,返回一个promise对象,而await可以认为是async wait的简写,等待一个异步方法执行完成。async必须声明的是一个function,await必须在声明的函数内部使用。

基本语法

 // async用于申明一个function是异步的,返回一个promise对象
    async function demo() {
        setTimeout(() => {
            console.log("我是async声明的一部函数");
        }, 1000);
        return "我是async声明的一部函数的返回值"
    }
    demo()
    console.log(demo());
    demo().then(res => {
        console.log(res);
    })
    // await 可以认为是 asunc wait 的简写,必须出现在async函数内部,不能单独使用
    // await 等待的虽然是promsie对象,但不必写.then(),直接可以得到返回值
    async function demo2() {
        let res1 = await Promise.resolve(777)
        console.log(res1);
    }
    demo2()

(2)、Async/await的应用

    Promise虽然一方面解决了callback的回调地狱,但是相对的把回调“纵向发展”了,形成了一个回调链。

应用:可以看出后面的结果都是依赖前面的结果,await是强制把异步变成了同步,这一句代码执行完,才会执行下一句

5.Module模块

(1)、模块化的初衷

现在的web系统越来越庞大、复杂,需要团队分工,多人协作,大型系统的javascript文件经常存在复杂的依赖关系,后期的维护成本会越来越高。

 JavaScript模块化正式为了解决这一需求而诞生。

vscode安装live sever

<script type="module">将script标签中的type的类型换成module,告诉浏览器我们要使用es6的模块化了

(2)、模块Module

模块Module:一个模块,就是一个对其他模块暴露自己的属性或者方法的文件。

在这里,我们会把module-A.js和module-B.js分别当作两个模块(moduleA模块和moduleB模块)来对待和处理。

(3)、导出Export

导出Export:作为一个模块,它可以选择性地给其他模块暴露(提供)自己的属性和方法,供其他模块使用。

(4)、导入Import

导入Import:作为一个模块,可以根据需要,引入其他模块的提供的属性或者方法,供自己模块使用。

(5)、模块化的实现

带着这三个概念,我们来演示一下它们的基本用法:

moduleB模块代码:

模块B我们使用关键字export关键字,对外暴露了一个属性:name的值为:字符串“前端君”

(6)、批量导出

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

(7)、重命名导出的变量

也许你突发奇想,想给导入的变量换一个名字的话,你可以这样做:

使用关键字as,可以实现给变量name更换名字为myname。最后正确输出myname的值:“前端君”。

(8)、整体导入

我们还可以使用星号*实现整体导入:

使用星号符*将模块B提供的所有属性和方法整体导入赋值给变量obj,我们可以点运算符来获取它的属性和方法。

(9)、默认导出

默认导出,每个模块支持我们导出一个没有名字的变量,我们使用关键语句export default来实现:

  我们使用export default关键字对外导出一个匿名函数,导入这个模块的时候,可以为这个匿名函数取任意的名字,我们试一下导入上面那个匿名函数:

同样是使用import关键字导入模块B,但是这次不需要使用大括号{ }。我们使用新的名字:sayDefault来代替导入的匿名函数,最后调用一下,打印结果正是模块B默认导出的匿名函数的执行效果。

(10)、注意事项

声明的变量,对外都是只读的。

上面的代码片段包含了2个模块,其中,模块B导出了字符串变量name,模块A导出变量name之后试图修改它的值,结果报错。

但是,如果模块B导出的是对象类型的值,就可修改。

 上面的代码片段包含了2个模块,模块B导出了对象person,模块A导入后,对其属性name进行修改,结果修改成功,这一点大家要注意,并不是所有导出的变量都不可修改,对象类型就可修改。

导入不存在的变量,会报错。

模块A想导入的变量height,在模块B中并没有提供,会抛出异常

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李时一

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值