• 李炎恢ECMAScript6 / ES6+(二)


    16.Set 数据集合

    学习要点:
    1.Set 数据集合
    本节课我们来开始学习 ES6 新增的 Set 数据集合的用法;

    一.Set 数据集合

    1.ES6 之前只有数组一种数据结构,而现在提供了 Set 和 Map 两种集合;

    严格来说,对象不算是一种数据结构

    先来打印看看它是什么

    let set = new Set();
    console.log(set);
    
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    2.Set 集合是一种无重复元素的列表,使用 new Set()方法创建 Set 集合

    //创建一个 Set 集合
    let set = new Set();
    set.add(1);
    
    set.add(2);//这两个合并成一个了
    set.add(2);
    
    set.add('2');
    set.add('c');
    console.log(set); //Set {1,2,'2','c'}
    console.log(set.size); //长度 4
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    3.我们也可以通过构造函数传递参数的方式进行初始化集合,比如接受一个数组

    //通过构造参数初始化集合
    let set = new Set([1, 2, 2, 3, 3, 4, 5]);
    console.log(set); //Set {1, 2, 3, 4, 5}
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    4.使用 has()方法查找是否存在指定元素,注意 2 和’2’是两个元素,不会隐式转换

    let set = new Set([1, 2, 2, 3, 3, 4, 5]);
    console.log(set.has(2)); //true
    console.log(set.has('2')); //false
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    5.还可以使用 delete()删除指定元素、clear()清空元素

    let set = new Set([1, 2, 2, 3, 3, 4, 5]);
    console.log(set); //{ 1, 2, 3, 4, 5 }
    console.log(set.delete(2)); //true
    console.log(set); //{ 1, 3, 4, 5 }
    console.log(set.clear());//undefined
    console.log(set);//{}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    6.我们可以使用…语法,将 Set 集合转换为数组

    let set = new Set([1, 2, 2, 3, 3, 4, 5]);
    let array = [...set];
    console.log(array);
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    7.我们可以使用 for 或者 forEach 来遍历 Set 集合

    let set = new Set([1, 2, 2, 3, 3, 4, 5]);
    for (let i of set){
        console.log(i);
    }
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    let set = new Set([1, 2, 2, 3, 3, 4, 5]);
    //forEach 变量
    //在 Set 集合中 key 和 value 都是值
    //s 表示 set 集合本身
    set.forEach(function (key, value, s) {
        console.log(value);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    8.Set 集合还提供针对对象的 Weak Set 集合,添加非对象类型会报错

    let ws = new WeakSet([1,2,3])
    
    • 1

    在这里插入图片描述

    9.Weak Set 集合支持 add()、has()和 delete()方法

    10.Weak Set 不支持遍历,内部隐藏(无法查看内容),不支持 foreach 和 size;

    11.对于应用场景来说,存放对象的弱引用,不用担心对象被回收后引发的问题;

    //强引用
    let set = new Set(),
        obj = {1:1};
    set.add(obj);
    console.log(set); //引用存在
    //移出引用
    obj = null;
    console.log(set); //引用依然存在
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    //弱引用
    let ws = new WeakSet(),
        obj = {1:1};
    ws.add(obj);
    console.log(ws.has(obj)); //引用存在,因为弱引用它是无法查看内部的情况的,只能通过这个方式查看是否存在
    //移出引用
    obj = null;
    console.log(ws.has(obj)); //随着销毁而释放
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    17.Map 数据集合

    学习要点:
    1.Map 数据集合
    本节课我们来开始学习 ES6 新增的 Map 数据集合的用法;

    一.Map 数据集合

    1.ES6 提供了 Map 数据集合,是一种以键值对存储的有序列表;

    let map = new Map();
    console.log(map);
    
    • 1
    • 2

    在这里插入图片描述

    //创建 Map 集合
    let map = new Map();
    map.set('name', 'Mr.Lee'); //.set 添加,支持.set(...).set(...)
    map.set('age', 100);
    console.log(map);
    console.log(map.get('name')); //.get 获取
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    2.我们也可以通过构造函数传递参数的方式进行初始化集合,比如接受一个数组

    //通过构造参数初始化集合
    let map = new Map([
        ['name', 'Mr.Lee'],
        ['age', 100]
    ]);
    console.log(map);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    3.使用 has()检测、delete()删除、clear()清空等对 Map 集合的操作

    let map = new Map([
        ['name', 'Mr.Lee'],
        ['age', 100]
    ]);
    console.log(map.has('name')); //true
    console.log(map.size); //2
    map.delete('name'); //删除
    map.clear(); //清空
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    4.我们可以使用 forEach 来遍历 Map 集合,至于 for 遍历,下个章节说

    let map = new Map([
        ['name', 'Mr.Lee'],
        ['age', 100]
    ]);
    
    map.forEach((value, key, m) => {
        console.log(key + '-' + value);
        console.log(m);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    5.Map 集合还提供针对对象的 Weak map 集合,添加非对象类型会报错

    let wm = new WeakMap();
    
    wm.set('1');
    
    console.log(wm);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    6.Weak Map 不支持遍历,内部隐藏(无法查看内容),不支持 foreach 和 size;

    let wm = new WeakMap(),
        obj = {};
    wm.set(obj);
    
    console.log(wm);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    7.对于应用场景来说,存放对象的弱引用,不用担心对象被回收后引发的问题

    和weak set一样

    18.迭代器和生成器

    学习要点:
    1.迭代器和生成器
    2.默认迭代接口
    本节课我们来开始学习 ES6 新增的迭代器和生成器的用法。

    一.迭代器和生成器

    1.迭代器(Iterator),用于给数据结构提供统一的访问遍历的机制

    2.ES6 之前的迭代器比较麻烦,而现在引入了生成器对象,让迭代器更加容易

    3.首先创建一个生成器方法,方法名前面加上*号,迭代的内容之前使用 yield;

    //生成器
    function *cit() {
    yield 1;
    yield 2;
    yield 3;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    PS:1,2,3 是我们要遍历的值;下面我们要创建迭代器;

    4.迭代器对象的.next()方法,类似指针,每次执行将下移一行;

    //迭代器
    let it = cit();
    //每执行一次.next()将下移一行
    console.log(it.next()); //1, false
    console.log(it.next()); //2, false
    console.log(it.next()); //3, false
    console.log(it.next()); //undefined, true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    PS:属性 value 得到值,没有返回 undefined,当没有值了,done 则返回 true;

    5.生成器结合循环语句,并且进行传递数组进行迭代

    function *cit(items) {
        for (let i = 0 ; items.length; i++) {
            yield items[i]
        }
    }
    let it = cit([1,2,3,4,5]);
    console.log(it.next().value);
    console.log(it.next().value);
    console.log(it.next().value);
    console.log(it.next().value);
    console.log(it.next().value);
    console.log(it.next().value);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    PS:如果作为匿名函数使用:let cit = function *(items);

    二.默认迭代接口

    1.很多数据结构类型拥有默认迭代接口,比如:Array、Map、Set 等等

    2.对于原生就支持迭代器的数据结构,我们不用自己编写生成器迭代器

    3.对于 Array 数组类型,它提供了有关三个方法:keys()、values()和 entries()

    let items = [1, 2, 3, 4, 5];
    console.log(items.keys()); //key, Object [Array Iterator]
    console.log(items.values()); //value, Object [Array Iterator]
    console.log(items.entries());//key+value, Object [Array Iterator]
    
    • 1
    • 2
    • 3
    • 4

    4.最简单的迭代方式,就是使用 for…of 迭代语句去遍历即可

    let items = [1, 2, 3, 4, 5];
    //for..of 遍历得到 value 值
    for (let i of items.values()) {
        console.log(i);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    5.虽然 for…of 特别方便,不过你想要用.next()语法也是支持的

    let items = [1, 2, 3, 4, 5];
    
    let values = items.values();
    console.log(values.next());
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    PS:下节课,我们把其它几种数据类型的默认迭代都演示一遍;

    19.异步 Promise

    学习要点:
    1.Promise 介绍
    2.实例测试
    本节课我们来开始学习 ES6 新增的 Promise 异步通信方案的功能。

    一.Promise 介绍

    1.Promise:即异步通信编程的一种解决方案,它比传统回调式更加的强大

    2.ES6 之前非常多层次嵌套的同步、异步,执行顺序混乱且不好维护

    3.Promise 就很好的解决了这些问题,我们先了解一下它的语法

    //创建一个 Promise 实例
    let p = new Promise((resolve, reject) => {
        //一顿异步通信操作后,返回成功或失败
        //然后判断成功或失败去执行 resolve 或 reject
        if (true) {
            //console.log('异步通信执行成功!');
            resolve('执行成功!');
        } else {
            //console.log('异步通信执行失败!');
            reject('执行失败!');
        }
    });
    
    
    //then 方法可执行 resolve 的回调函数
    //catch 方法可执行 reject 的回调函数
    // p.then((value) => {
    //     console.log(value);
    // }).catch((reason) => {
    //     console.log(reason);
    // });
    
    //或者这种一体化语法也行
    p.then((value) => {
        console.log(value);
    },(reason)=>{
        console.log(reason);
    });
    
    
    • 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

    在这里插入图片描述

    PS:如果你有过很多层异步通信实战基础,上面提供的方法会突然感觉清晰很多;
    PS:因为它把多层嵌套的回调函数给分离出来,通过 then 和 catch 来实现;

    3.通过上面例子的语法,我们发现 p 作为 Promise 实例,可以进行连缀链式操作

    4.当执行了 then 方法后,本身依旧返回了当前的 Promise 实例,方便链式

    5.注释中也说明了,通过构造方法的两个参数去执行回调函数,并传递参数

    6.事实上,catch()方法还可以作为 then 第二参数进行存在,方便多层回调

    ...
    p.then((value) => {
        console.log(value);
    },(reason)=>{
        console.log(reason);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    二.实例测试

    1.我们做个模拟多层异步通信的实例测试,要异步多个内容,并按指定顺序执行

    2.先给出不进行 Promise 异步,看它执行的顺序

    //模拟异步 1
    setTimeout(() => {
        console.log('1.返回异步通信');
    }, 3500);
    //模拟异步 2
    setTimeout(() => {
        console.log('2.返回异步通信');
    }, 800);
    //模拟异步 3
    setTimeout(() => {
        console.log('3.返回异步通信');
    }, 1500);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述
    PS:这里不管你怎么调节,最终输出结果总是:2,3,1。需求顺序要:1,2,3;

    3.将上面模拟异步通信,通过 Promise 进行改装,再看看执行结果

    let p1 = new Promise((resolve, reject) => {
    //模拟异步 1
        setTimeout(() => {
    //console.log('1.异步通信');
            resolve('1.返回异步通信');
        }, 3500);
    });
    let p2 = new Promise((resolve, reject) => {
    //模拟异步 2
        setTimeout(() => {
    //console.log('2.异步通信');
            resolve('2.返回异步通信');
        }, 800);
    });
    let p3 = new Promise((resolve, reject) => {
    //模拟异步 3
        setTimeout(() => {
    //console.log('3.异步通信');
            resolve('3.返回异步通信');
        }, 1500);
    });
    //执行回调
    p1.then((value) => {
        console.log(value);
        return p2;
    }).then((value) => {
        console.log(value);
        return p3;
    }).then((value) => {
        console.log(value);
    });
    
    • 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

    在这里插入图片描述

    20.Promise 状态特点

    学习要点:
    1.状态特点
    2.更多方法
    本节课我们来开始学习 ES6 新增的 Promise 异步通信方案的状态特点

    一.状态特点

    1.回顾上一节:Promise 解决了异步多层回调混乱,且执行顺序的问题

    2.本节课,了解一下 Promise 对象异步操作的三种状态

    (1) .Pending(进行中)
    (2) .Fulfilled(已成功)
    (3) .Rejected(已失败)

    3.当异步操作执行后,它得到的结果来决定其状态,其它任何操作都无法改变

    4.Promise 状态只有两种运行方式:从 Pending 到 Fulfilled 或 Rejected;

    5.而当状态已经固定后,此时就变成 Resolved(已完成)。关键字详解:请搜索;

    pending -> resolve 方法 -> fulfilled -> resolved
    pending -> reject 方法 -> rejected -> resolved

    PS:测试当前状态,在浏览器环境下比较直观直接:console.log(p1),在不同阶段执行;node环境下看得不明显,用浏览器测试

    let p1 = new Promise((resolve, reject) => {
    //模拟异步 1
        setTimeout(() => {
    //console.log('1.异步通信');
            resolve('1.返回异步通信');
        }, 3500);
    });
    let p2 = new Promise((resolve, reject) => {
    //模拟异步 2
        setTimeout(() => {
    //console.log('2.异步通信');
            resolve('2.返回异步通信');
        }, 800);
    });
    let p3 = new Promise((resolve, reject) => {
    //模拟异步 3
        setTimeout(() => {
    //console.log('3.异步通信');
            resolve('3.返回异步通信');
        }, 1500);
    });
    
    console.log(p1);
    
    
    //执行回调
    p1.then((value) => {
        console.log(p1);
        console.log(value);
        return p2;
    }).then((value) => {
        console.log(value);
        return p3;
    }).then((value) => {
        console.log(value);
    });
    
    • 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

    在这里插入图片描述

    二.更多方法

    1.上一节课,我们使用了三组 Promise 实例完成三段异步的排序输出问题

    但是那个看起来还是太长了,下面有好的解决方案

    2.Promise 提供了一个 all()方法,可以简化多个实例调用输出排序

    
    
    let p1 = new Promise((resolve, reject) => {
    //模拟异步 1
        setTimeout(() => {
    //console.log('1.异步通信');
            resolve('1.返回异步通信');
        }, 3500);
    });
    let p2 = new Promise((resolve, reject) => {
    //模拟异步 2
        setTimeout(() => {
    //console.log('2.异步通信');
            resolve('2.返回异步通信');
        }, 800);
    });
    let p3 = new Promise((resolve, reject) => {
    //模拟异步 3
        setTimeout(() => {
    //console.log('3.异步通信');
            resolve('3.返回异步通信');
        }, 1500);
    });
    
    console.log(p1);
    
    
    //执行回调
    // p1.then((value) => {
    //     console.log(p1);
    //     console.log(value);
    //     return p2;
    // }).then((value) => {
    //     console.log(value);
    //     return p3;
    // }).then((value) => {
    //     console.log(value);
    // });
    
    //all方法,可以解决上面那一大堆, p1,p2,p3 是三个 Promise 实例,数组元素顺序即输出顺序
    let p = Promise.all([p1, p2, p3]);
    //将三个 Promise 实例的回调组合成数组输出
    p.then(value => {
        console.log(value);
    })
    
    • 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

    在这里插入图片描述
    PS:虽然 p1,p2,p3 都是异步操作,但最终要等待所有异步完成,才可以输出;
    PS:只要 p1,p2,p3 中有一个出现了 Rejected,则会执行失败回调

    3.Promise 提供了一个 race()方法,只输出第一个改变状态的实例

    //p1,p2,p3 只要有一个改变状态,即回调
    let p = Promise.race([p1, p2, p3]);
    //所以,这里只输出 p2
    p.then(value => {
        console.log(value);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    4.Promise 提供了 resolve()和 reject(),直接返回一个成功或失败的实例

    //直接返回成功或失败的 Promise 实例
    let ps = Promise.resolve('成功');
    let pj = Promise.reject('失败');
    ps.then(value => {
    console.log(value);
    return pj;
    }).catch(reason => {
    console.log(reason);
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    等价于

    new Promise(resolve => resolve('成功'));
    
    • 1

    那这样的快捷语法糖有什么用呢?下面是一个例子

    function getP() {
    if (false) {
    return new Promise(resolve => {
    resolve('异步成功');
    })
    } else {
    return 0; //强制类型一致保证程序正确性 Promise.resolve(0)
    }
    }
    
    getP().then(value => {
    console.log(value);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    这样就可能出问题,因为你很有可能会读取失败,我们要保持返回类型的一致性,就可以通过这种语法糖快速调用 resolve回调

    function getP() {
    if (false) {
    return new Promise(resolve => {
    resolve('异步成功');
    })
    } else {
    //return 0; //强制类型一致保证程序正确性 Promise.resolve(0)
    return Promise.resolve(0);
    }
    }
    
    //这里你调用.then就肯定可以成功
    getP().then(value => {
    console.log(value);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    21.代理 Proxy

    学习要点:
    1.代理能力
    本节课我们来开始学习 ES6 新增的 Proxy 代理方法。

    一.代理能力

    1.什么是代理?即:给目标对象封装一层拦截,外界访问必须先通过这层拦截

    2.举个例子:猎头招聘,你自己发布招聘会暴露自身信息,而通过中介则安全的多

    3.首先,我们先来看下代理 Proxy 的语法

    //目标对象
    let obj = {
        name : 'Mr.Lee',
        age : 100,
        gender : '男'
    };
    //创建一个代理,参数 1 拦截的目标对象,参数 2 拦截行为
    //参数 2 如果是空对象,代理直接会调用目标对象
    let p = new Proxy(obj, {
    //get 方法用于拦截某个属性的读取操作
        //这里直接 return,通过代理对象无论访问目标对象的任何属性都是 fail
        get(target, property) {
            return 'fail';
        },
    });
    //代理对象访问 name 为 fail
    console.log(p.name);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述

    4.如果想让代理对象公布出合适的信息,可以通过 get()两个参数来实现

    //目标对象
    let obj = {
        name : 'Mr.Lee',
        age : 100,
        gender : '男'
    };
    //创建一个代理,参数 1 拦截的目标对象,参数 2 拦截行为
    //参数 2 如果是空对象,代理直接会调用目标对象
    let p = new Proxy(obj, {
        //get 方法用于拦截某个属性的读取操作
        get(target, property) {
            if (property === 'age') {
                return target[property] - 80;
            }
        },
    });
    console.log(p.name);//undefined
    console.log(p.age);//20
    console.log(p.abc);//不存在的属性 undefined
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    4.我们也可以通过 set()方法来对代理对象的属性进行赋值,有三个参数

    //目标对象
    let obj = {
        name : 'Mr.Lee',
        age : 100,
        gender : '男'
    };
    //创建一个代理,参数 1 拦截的目标对象,参数 2 拦截行为
    //参数 2 如果是空对象,代理直接会调用目标对象
    let p = new Proxy(obj, {
        //get 方法用于拦截某个属性的读取操作
        get(target, property) {
            if (property === 'age') {
                return target[property];
            }
        },
    
        //set 可以拦截某个属性的赋值操作,比 get 多了参数 3
        set(target, property, value) {
            if (property === 'age') {
                if (!Number.isInteger(value) || value > 150) {
                    throw new TypeError('年龄数据不合法!');
                }
                target[property] = value;
            }
        }
    });
    
    p.age = 150;
    // p.age = 200;//年龄数据不合法!
    
    console.log(p.age);//150
    console.log(obj.age); //150 目标对象属性也被更改
    
    • 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

    在这里插入图片描述

    PS:代理并不是复制克隆目标对象,只是拦截目标对象更改默认行为;
    PS:代理可以使用 set()和 get()方法,对目标对象的数据进行过滤和验证;
    PS:代理对象中任何未公开或不存在的属性,可自定义返回内容,比如:fail 或已屏蔽;
    PS:代理也可以阻止赋值的默认行为:直接 return false,就禁止赋值了;

    set(target, property, value) {
    return false;
    }
    
    • 1
    • 2
    • 3

    22.异步 async

    学习要点:
    1.async 语法
    本节课我们来开始学习 ES8 新增的异步 async 方法。

    一.async 语法

    1.async 也是处理异步的,它是对 Promise 的一种扩展,让异步更加方便

    2.优势:async 是基于 Promise 的,虽然是异步操作,但看上去像同步

    3.首先,我们先来看下 async 的基本语法

    let p1 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('1.返回异步通信');
        }, 3500);
    });
    let p2 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('2.返回异步通信');
        }, 800);
    });
    let p3 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('3.返回异步通信');
        }, 1500);
    });
    
    
    //创建一个 async 函数,执行异步操作
    //await 关键字:等待异步执行完毕后回调;
    let as = async () => {
        let result = await p1;
        console.log(result);
    };
    //执行 async 函数
    as();
    
    PS:上面语法,用 ES5 过渡一下,帮助理解,具体如下:
    async function as() {}
    let as = async function () {}
    let obj = {async as() {}}; //也支持对象方式
    
    
    • 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

    在这里插入图片描述

    4.如果有三个异步需要列队输出,我们用 async 语法来处理一下

    //多个异步,按输出顺序加载,没有 then,清晰很多
    async function as() {
        let r1 = await p1,
            r2 = await p2,
            r3 = await p3;
        console.log(r1);
        console.log(r2);
        console.log(r3);
    }
    as();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述
    PS:await 关键字只能在 async 函数内部,否则不可识别;
    PS:从上面的例子中,能感受到语义和清晰度都得到了很大提升,更像同步代码;

    5.批量异步列队,类似 Promise.all()

    async function as() {
        let all = [await p1, await p2, await p3];
        console.log(all);
    }
    as();
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    6.async 函数如果设置了返回值,这个值是 Promise 对象

    //返回值是 Promise 对象
    //相当于 Promise.resolve()
    async function as() {
        return 'hello, async!';
    }
    
    //所以还需要 Promise 对象的then
    as().then(value => {
        console.log(value);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    7.如果 return await p;这种,会导致提前输出 pending 状态,还是需要 then;

    async function as() {
        return await p1;
    }
    console.log(as()); //得到的是 Promise 对象的 pending 状态
    as().then(value => { //这里还是需要 then
        console.log(value);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    23.类 class 实现

    学习要点:
    1.类 class
    2.getter 和 setter
    本节课我们来开始学习 ES6 新增的类 class 的实现方法。

    一.类 class

    1.在 ES6 之前,JavaScript 不能像其它语言(PHP,Java)等有完整的类支持;

    2.我们采用了原型链实现了面向对象的功能,但从 ES6 开始,提供了真正的类语法

    3.当然,虽然说是真正的类语法,而本质上内部实现和原型链还是一样的;

    4.对于面向对象和类的基础概念,这里不再赘述,学到这里不可能没这个基础的

    5.首先,我们创建一个基本的类,并创建构造函数(构造方法),具体如下

    //创建一个类
    class Person {
        //构造函数(构造方法)
        constructor(name) {
        //this.name 是类的属性
        //name 是构造参数赋值给属性
            this.name = name;
        }
        //普通方法
        run() {
            console.log('类的方法输出!' + this.name);
        }
    }
    
    //实例化一个 Person 对象
    let p = new Person('Mr.Lee');
    //执行 run()方法
    p.run();
    
    //给成员属性赋值
    p.name = 'Mr.wang';
    
    //输出对象的属性
    console.log(p.name);
    //判断 p 是否是 Person 对象
    console.log(p instanceof Person);
    //判断类的类型:function
    console.log(typeof Person);
    
    • 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

    在这里插入图片描述

    6.除了上面的 class Person 这种常规类的写法外,ES6 还支持表达式写法

    注意:这里是javascropt才有的写法

    //No.1
    let Per = class Person {};
    //此时 new Person 会报错
    let p = new Per('Mr.Lee');
    //No.2
    let Person = class {};
    //No.3
    let p = new class {}('Mr.Lee');
    p.run();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    二.getter 和 setter

    1.根据面向对象的三大定律中成员属性,我们需要对它进行封装,变成私有属性

    2.而目前的 this.name,基本是对外公开的,可以在类外取值和赋值

    3.当我们假设类的属性是私有的,那么需要通过 get()和 set()方法实现

    // //创建一个类
    // class Person {
    //     //构造函数(构造方法)
    //     constructor(name) {
    //     //this.name 是类的属性
    //     //name 是构造参数赋值给属性
    //         this.name = name;
    //     }
    //     //普通方法
    //     run() {
    //         console.log('类的方法输出!' + this.name);
    //     }
    // }
    //
    // //实例化一个 Person 对象
    // let p = new Person('Mr.Lee');
    // //执行 run()方法
    // p.run();
    //
    // //给成员属性赋值
    // p.name = 'Mr.wang';
    //
    // //输出对象的属性
    // console.log(p.name);
    // //判断 p 是否是 Person 对象
    // console.log(p instanceof Person);
    // //判断类的类型:function
    // console.log(typeof Person);
    
    //创建一个类
    class Person {
        #name; //提案,浏览器暂时不支持
        //构造函数(构造方法)
        constructor(name) {
            this.#name = name; //私有属性,类外无法访问
        }
        get name() {
            return this.#name;
        }
        set name(value) {
            this.#name = value;
        }
    }
    let p = new Person('Mr.Lee');
    //
    // // p.#name = 'Mr.Wang'; // Private field '#name' must be declared in an enclosing class 直接不给修改
    //
    // p.name = 'Mr.Wang';
    // console.log(p.name); //为什么这两句可以呢?
    
    //因为上面两句,其实它并没有访问私有成员属性,而是自己创建了一个属性name,就相当于下面的代码
    // let obj = {};
    // obj.name = 'Mr.wang';
    // console.log(obj.name);
    
    
    //所以这就是容易混淆的地方,此时我们应该访问的是 #name, 那么#name 是私有的属性,那么我们应该怎么访问和修改呢?同时又要拦截外部 name的访问?
    //利用 get set 同时名字取成和name相同的名字,就可以防止外部修改了。
    
    
    
    
    
    
    • 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
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    24.类 class 继承

    学习要点:
    1.类的继承
    本节课我们来开始学习 ES6 新增的类 class 的继承功能

    一.类的继承

    1.ES6 也支持子类继承父类,使用 extends 关键字实现

    class Person {
    
        constructor(name) {
            this.name = name;
        }
    
        get user() {
            return this.name;
        }
    
        set user(value){
            this.name = value;
        }
    
        run() {
            return 'name:' + this.name;
        }
    
    }
    
    class Son extends Person {
    
    }
    
    
    //可以使用父类的构造
    let c = new Son('Mr.li');
    console.log(c.name);
    
    //get,set 以及方法均可使用
    c.user = 'Mr.Wang';
    console.log(c.user);
    console.log(c.run());
    
    
    • 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

    在这里插入图片描述

    2.继承之后,一般来说,我们需要覆写父类,然后对子类进行增强

    class Person {
    
        constructor(name) {
            this.name = name;
        }
    
        get user() {
            return this.name;
        }
    
        set user(value){
            this.name = value;
        }
    
        run() {
            return 'name:' + this.name;
        }
    
    }
    
    class Son extends Person {
    //子类继承
        constructor(name, age) { //覆写构造
            super(name); //执行父类构造并传参
            this.age = age;
        }
        run() { //覆写方法
            return super.run() + this.age; //执行父类方法并返回内容
        }
    }
    //覆写
    let son = new Son('Mr.Lee', 100);
    console.log(son.age);//子类构造自己传递进来的age
    console.log(son.run());//先执行父类的方法,然后再执行子类自己的处理
    
    • 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

    在这里插入图片描述

    3.可以使用 Object.getPrototypeOf()判断子类是否继承了父类

    console.log(Object.getPrototypeOf(Children) === Person);
    
    • 1

    在这里插入图片描述

    4.ES6 的类支持静态属性和方法,也支持静态被子类继承

    class Person {
        static gender = '男';
        static go() {
            return 'GO GO GO !' + Person.gender;//类名调用静态属性
        }
    }
    class Children extends Person {
        static gender = '女'; //覆写静态
        static go() {
            return 'o o o ' + Person.gender;//这里使用 Person 和 Children 是不一样的  如果这里使用 this.就等于Children类
        }
    }
    
    console.log(Person.gender);//男
    console.log(Person.go());//调用Person的静态方法
    
    
    console.log(Children.gender);
    console.log(Children.go());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    拓展:es5演示静态方法和实例方法

    let Animal = function (type) {
      this.type = type
    }
     
    // 动态方法
    Animal.prototype.walk = function(){
      // 调用静态方法
      Animal.eat()
      console.log('I am walking')
    }
     
    // 静态方法
    Animal.eat = function (food) {
      console.log(`I am eating`);
    }
     
    let cat = new Animal('cat')
    cat.walk()
    cat.eat()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    25.Module 模块化

    学习要点:
    1.浏览器加载
    2.Node 加载
    本节课我们来开始学习 ES6 新增的模块化导入导出的方法。

    一.ES6 模块化

    0.在以前我们是怎么做的?

    在这里插入图片描述

    1.ES6 支持模块化设计,也能其它后端语言一样使用导入导出的功能

    2.我们首先,创建一个要被导入的模块 module.js,具体如下

    export let name = 'Mr.Lee'; //导出这个变量
    
    • 1

    PS:里面有很多变量,导出那个那个才可以用,其它不可见

    3.再创建一个普通的.js 文件,比如 25.js,然后导入 module.js;

    import {name} from './module.js';
    console.log(name);
    
    • 1
    • 2

    4.最后一步,在.html 文件要加载 25.js 文件,才有小,注意 type 格式;

    <script type="module" src="js/25.js"></script>
    
    • 1

    PS:注意,这种导入导出的方式属于 ES6 模块,仅支持浏览器模式;
    如果不设置type="module"则会报错
    在这里插入图片描述
    在这里插入图片描述

    5.除了导出变量、常量之外,还可以导出函数、类等功能

    export let name = 'Mr.Lee'; 
    
    export function sum(x, y) {
        return x + y;
    }
    export class Person {
        constructor(name) {
            this.name = name;
        }
        run() {
            return 'name : ' + this.name;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    import {name, sum, Person} from './module.js';
    console.log(name);
    console.log(sum(10, 20));
    console.log((new Person('Mr.Lee')).run());
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    6.也支持使用*号,将所有导出的内容全部加载进来

    import * as obj from './module.js';
    console.log(obj.name);
    console.log(obj.sum(10, 20));
    console.log((new obj.Person('Mr.Lee')).run());
    
    • 1
    • 2
    • 3
    • 4

    7.支持别名设定,设定别名后,源名即失效了

    import {name as user} from './module.js';
    console.log(user); //name 无效了
    
    • 1
    • 2

    8.统一导出方案,不需要再每一个导出的内容设置 export

    let name = 'Mr.Lee';
    
    function sum(x, y) {
        return x + y;
    }
    
    class Person {
        constructor(name) {
            this.name = name;
        }
    
        run() {
            return 'name : ' + this.name;
        }
    }
    
    export {
        name,
        sum,
        Person
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述

    9.可以给导出设置一个默认值,导入部分就可以不用花括号了

    export default name;
    
    import name from './module.js';//导入得时候直接name就可以了
    
    import name, {sum, Person} from './module.js';//如果还要引入别的,逗号,+花括号
    
    • 1
    • 2
    • 3
    • 4
    • 5

    二.Node 加载

    1.Node 下有自己的导出和导入的加载模式:CommonJS 规范

    新建common.js文件

    let name = 'Mr.Lee';
    module.exports = {
    name : name,
    };
    const name = require('./common.js');
    console.log(name);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    Spring系列:Spring6简介和基本使用
    Java深度学习库DJL实现Python的NumPy
    [MAUI]写一个跨平台富文本编辑器
    JDBC-day03(BLOB类型字段,批量插入)
    vue移动端项目渲染pdf步骤
    c++征途 --- STL初识
    开放原子训练营第三季——基于UBML开源项目的免费企业级低代码平台inBuilder低代码开发实验室圆满落幕!
    leetcode 45. 跳跃游戏 II(dp + 贪心)
    美团三年,总结的10条血泪教训
    Netlogo 简化版Scatter 分散
  • 原文地址:https://blog.csdn.net/qq_35081380/article/details/128140083