ES6_新特性

let

let关键字用来声明变量,使用 let声明的变量有几个特点:
  1) 不允许重复声明
  2) 块级作用域
  3) 不存在变量提升
  4) 不影响作用域链

     <script>
        //声明变量
        let a;
        let b,c,d;
        let e = 100;
        let f = 521, g = 'iloveyou', h = [];
        //1. 变量不能重复声明
        // let star = '罗志祥';
        // let star = '小猪';
        //2. 块级作用域  全局, 函数, eval
        // if else while for 
        // {
        //     let girl = '周扬青';
        // }
        // console.log(girl);
        //3. 不存在变量提升
        // console.log(song);
        // let song = '恋爱达人';
        //4. 不影响作用域链
        {
            let school = '清华';
            function fn(){
                console.log(school);
            }
            fn();
        }
    </script>

经典案例

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>点击 DIV 换色</title>
        <link
            crossorigin="anonymous"
            href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css"
            rel="stylesheet"
        />
        <style>
            .item {
                width: 100px;
                height: 50px;
                border: solid 1px rgb(42, 156, 156);
                float: left;
                margin-right: 10px;
            }
        </style>
    </head>

    <body>
        <div class="container">
            <h2 class="page-header">点击切换颜色</h2>
            <div class="item"></div>
            <div class="item"></div>
            <div class="item"></div>
        </div>
        <script>
            //获取div元素对象
            let items = document.getElementsByClassName('item')
            //遍历并绑定事件
            for (let i = 0; i < items.length; i++) {
                items[i].onclick = function () {
                    //修改当前元素的背景颜色
                    // this.style.background = 'pink';
                    items[i].style.background = 'pink'
                }
            }
        </script>
    </body>
</html>

const

const 关键字用来声明常量, const声明有以下特点
  1) 声明必须赋初始值
  2) 标识符一般为大写
  3) 不允许重复声明
  4) 值不允许修改
  5) 块级作用域
  注意:对象属性修改和数组元素变化不会触发const错误
  应用场景:声明对象类型使用const,非对象类型声明选择 let

    <script>
        //声明常量
        const SCHOOL = '清华';
        //1. 一定要赋初始值
        // const A;
        //2. 一般常量使用大写(潜规则)
        // const a = 100;
        //3. 常量的值不能修改
        // SCHOOL = 'QINGHUA';
        //4. 块儿级作用域
        // {
        //     const PLAYER = 'UZI';
        // }
        // console.log(PLAYER);
        //5. 对于数组和对象的元素修改, 不算做对常量的修改, 不会报错
        const TEAM = ['UZI','MXLG','Ming','Letme'];
        // TEAM.push('Meiko');
    </script>

变量的解构赋值

ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。
注意:频繁使用对象方法、数组元素,就可以使用解构赋值形式

    <script>
        //1. 数组的结构
        // const F4 = ['小沈阳','刘能','赵四','宋小宝'];
        // let [xiao, liu, zhao, song] = F4;
        // console.log(xiao);
        // console.log(liu);
        // console.log(zhao);
        // console.log(song);

        //2. 对象的解构
        // const zhao = {
        //     name: '赵本山',
        //     age: '不详',
        //     xiaopin: function(){
        //         console.log("我可以演小品");
        //     }
        // };
        // let {name, age, xiaopin} = zhao;
        // console.log(name);
        // console.log(age);
        // console.log(xiaopin);
        // xiaopin();
    </script>

模板字符串

模板字符串(template string)是增强版的字符串,用反引号(`)标识 ,

特点:
  1) 字符串中可以出现换行符
  2) 可以使用 ${xxx} 形式输出变量

注意:当遇到字符串与变量拼接的情况可以使用模板字符串

    <script>
        //1. 声明
        // let str = `我也是一个字符串哦!`;
        // console.log(str, typeof str);
        //2. 内容中可以直接出现换行符
        let str = `<ul>
                    <li>沈腾</li>
                    <li>玛丽</li>
                    <li>魏翔</li>
                    <li>艾伦</li>
                    </ul>`;
        //3. 变量拼接
        let lovest = '魏翔';
        let out = `${lovest}是我心目中最搞笑的演员!!`;
        console.log(out);
    </script>

简化对象写法

ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。

    <script>
        let name = '新东方';
        let change = function(){
            console.log('我们可以改变你!!');
        }
        const school = {
            name,
            change,
            improve(){
                console.log("我们可以提高你的技能");
            }
        }
        console.log(school);
    </script>

箭头函数

ES6 允许使用 「 箭头 」(=>)定义函数。

注意点:

  1) 如果形参只有一个,则小括号可以省略
  2) 函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果
  3) 箭头函数this指向声明时所在作用域下this 的值
  4) 箭头函数不能作为构造函数实例化
  5) 不能使用arguments

注意:箭头函数不会更改this指向,用来指定回调函数会非常合适

     <script>
        //声明一个函数
        // let fn = function(){ }
        // let fn = (a,b) => {
        //     return a + b;
        // }
        //调用函数
        // let result = fn(1, 2);
        // console.log(result);
        //1. this 是静态的. this 始终指向函数声明时所在作用域下的 this 的值
        function getName(){
            console.log(this.name);
        }
        let getName2 = () => {
            console.log(this.name);
        }
        //设置 window 对象的 name 属性
        window.name = '新东方';
        const school = {
            name: "XINDONGFANG"
        }
        //直接调用
        // getName();
        // getName2();
        //call 方法调用
        // getName.call(school);
        // getName2.call(school);

        //2. 不能作为构造实例化对象
        // let Person = (name, age) => {
        //     this.name = name;
        //     this.age = age;
        // }
        // let me = new Person('xiao',30);
        // console.log(me);

        //3. 不能使用 arguments 变量
        // let fn = () => {
        //     console.log(arguments);
        // }
        // fn(1,2,3);

        //4. 箭头函数的简写
        //1) 省略小括号, 当形参有且只有一个的时候
        // let add = n => {
        //     return n + n;
        // }
        // console.log(add(9));
        //2) 省略花括号, 当代码体只有一条语句的时候, 此时 return 必须省略
        // 而且语句的执行结果就是函数的返回值
        let pow = n => n * n;
        console.log(pow(8));
    </script>

经典案例

    <script>
        //需求-1  点击 div 2s 后颜色变成『粉色』
        //获取元素
        let ad = document.getElementById('ad');
        //绑定事件
        ad.addEventListener("click", function(){
            //保存 this 的值
            // let _this = this;
            //定时器
            setTimeout(() => {
                //修改背景颜色 this
                // console.log(this);
                // _this.style.background = 'pink';
                this.style.background = 'pink';
            }, 2000);
        });

        //需求-2  从数组中返回偶数的元素
        const arr = [1,6,9,10,100,25];
        // const result = arr.filter(function(item){
        //     if(item % 2 === 0){
        //         return true;
        //     }else{
        //         return false;
        //     }
        // });
        const result = arr.filter(item => item % 2 === 0);
        console.log(result);

        // 箭头函数适合与 this 无关的回调. 定时器, 数组的方法回调
        // 箭头函数不适合与 this 有关的回调.  事件回调, 对象的方法
    </script>

参数默认值

ES6允许给函数参数赋值初始值
1. 形参初始值具有默认值的参数, 一般位置要靠后(潜规则)
2. 与解构赋值结合
     <script>
        //1. 形参初始值 具有默认值的参数, 一般位置要靠后(潜规则)
        // function add(a,c=10,b) {
        //     return a + b + c;
        // }
        // let result = add(1,2);
        // console.log(result);

        //2. 与解构赋值结合
        function connect({host="127.0.0.1", username,password, port}){
            console.log(host)
            console.log(username)
            console.log(password)
            console.log(port)
        }
        connect({
            host: 'baidu.com',
            username: 'root',
            password: 'root',
            port: 3306
        })
    </script>

rest参数

ES6引入rest参数,用于获取 函数的实参,用来代替 arguments。

注意:rest参数非常适合不定个数参数函数的场景

    <script>
        // ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments
        // ES5 获取实参的方式
        // function date(){
        //     console.log(arguments);
        // }
        // date('白芷','阿娇','思慧');

        // rest 参数
        // function date(...args){
        //     console.log(args);// filter some every map 
        // }
        // date('阿娇','柏芝','思慧');

        // rest 参数必须要放到参数最后

        function fn(a, b, ...args) {
            console.log(a);
            console.log(b);
            console.log(args);
        }
        fn(1, 2, 3, 4, 5, 6);
    </script>

扩展运算符

扩展运算符(spread)也是三个点 (...)。

它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,对数组进行解包。

    <script>
        // 『...』 扩展运算符能将『数组』转换为逗号分隔的『参数序列』
        //声明一个数组 ...
        const tfboys = ['易烊千玺','王源','王俊凯'];
        // => '易烊千玺','王源','王俊凯'

        // 声明一个函数
        function chunwan(){
            console.log(arguments);
        }
        chunwan(...tfboys);// chunwan('易烊千玺','王源','王俊凯')      
    </script>

经典案例

   <script>
        //1. 数组的合并 情圣  误杀  唐探
        // const kuaizi = ['王太利','肖央'];
        // const fenghuang = ['曾毅','玲花'];
        // // const zuixuanxiaopingguo = kuaizi.concat(fenghuang);
        // const zuixuanxiaopingguo = [...kuaizi, ...fenghuang];
        // console.log(zuixuanxiaopingguo);

        //2. 数组的克隆
        // const sanzhihua = ['E','G','M'];
        // const sanyecao = [...sanzhihua];//  ['E','G','M']
        // console.log(sanyecao);

        //3. 将伪数组转为真正的数组
        const divs = document.querySelectorAll('div');
        const divArr = [...divs];
        console.log(divArr);// arguments  
    </script>

 迭代器

遍历器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。

  1) ES6创造了一种新的遍历命令 for...of循环, Iterator接口主要供 for...of消费

  2) 原生具备 iterator接口的数据 (可用 for of遍历 )

    a) Array
    b) Arguments
    c) Set
    d) Map
    e) String
    f) TypedArray
    g) NodeList
  3) 工作原理
    a) 创建一个指针对象,指向当前数据结构的起始位置
    b) 第一次调用对象的 next方法,指针自动指向数据结构的第一个成员
    c) 接下来不断调用 next方法,指针一直往后移动,直到指向最后一个成员
    d) 每调用 next方法返回一个包含 value和 done属性的对象
注: 需要自定义遍历数据的时候,要想到迭代器。

案例可在我的另一篇文章《ES6_Symbol》的Symbol.iterator模块查看。

生成器

生成器函数是 ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同。

代码说明:
  1) * 的位置没有限制
  2) 生成器函数返回的结果是迭代器对象,调用迭代器对象的 next方法可以得到yield语句后的值
  3) yield相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次 next方法,执行一段代码
  4) next方法可以传递实参,作为 yield语句的返回值

生成器函数

    <script>
        //生成器其实就是一个特殊的函数
        //异步编程  纯回调函数  node fs  ajax mongodb
        //函数代码的分隔符
        function* gen() {
            console.log(111);
            yield '一只没有耳朵';
            console.log(222);
            yield '一只没有尾部';
            console.log(333);
            yield '真奇怪';
            console.log(444);
        }

        // let iterator = gen();
        // iterator.next();
        // console.log(iterator.next());
        // iterator.next();
        // console.log(iterator.next());
        // iterator.next();
        // console.log(iterator.next());
        // iterator.next();
        // console.log(iterator.next());

        //遍历
        for (let v of gen()) {
            console.log(v);
        }
    </script>

 生成器函数参数

    <script>
        function* gen(arg) {
            console.log(arg);
            let one = yield 111;
            console.log(one);
            let two = yield 222;
            console.log(two);
            let three = yield 333;
            console.log(three);
        }

        //执行获取迭代器对象
        let iterator = gen('AAA');
        console.log(iterator.next());
        //next方法可以传入实参
        console.log(iterator.next('BBB'));
        console.log(iterator.next('CCC'));
        console.log(iterator.next('DDD'));
    </script>

 

 生成器函数实例

    <script>
        //模拟获取  用户数据  订单数据  商品数据 
        function getUsers() {
            setTimeout(() => {
                let data = '用户数据';
                //调用 next 方法, 并且将数据传入
                iterator.next(data);
            }, 1000);
        }

        function getOrders() {
            setTimeout(() => {
                let data = '订单数据';
                iterator.next(data);
            }, 1000)
        }

        function getGoods() {
            setTimeout(() => {
                let data = '商品数据';
                iterator.next(data);
            }, 1000)
        }

        function* gen() {
            let users = yield getUsers();
            console.log(users);
            let orders = yield getOrders();
            console.log(orders);
            let goods = yield getGoods();
            console.log(goods);
        }

        //调用生成器函数
        let iterator = gen();
        iterator.next();
    </script>

Set

ES6 提供了新的数据结构 Set(集合) 。它类似于数组,但成员的值都是唯一的 ,集合实现了 iterator接口,所以可以使用『扩展运算符』和『 for…of…』进行遍历,集合的属性和方法:
  1) size返回集合的元素个数
  2) add增加一个新元素,返回当前集合
  3) delete删除元素,返回 boolean 值
  4) has检测集合中是否包含某个元素,返回 boolean值
  5) clear清空集合,返回 undefined

<script>
    //声明一个 set
    let s = new Set();
    let s2 = new Set(['大事儿','小事儿','好事儿','坏事儿','小事儿']);
    //元素个数
    // console.log(s2.size);
    //添加新的元素
    // s2.add('喜事儿');
    //删除元素
    // s2.delete('坏事儿');
    //检测
    // console.log(s2.has('糟心事'));
    //清空
    // s2.clear();
    // console.log(s2);
    for(let v of s2){
        console.log(v);
    }
</script>

经典案例

<script>
    let arr = [1,2,3,4,5,4,3,2,1];
    //1. 数组去重
    // let result = [...new Set(arr)];
    // console.log(result);
    //2. 交集
    let arr2 = [4,5,6,5,6];
    // let result = [...new Set(arr)].filter(item => {
    //     let s2 = new Set(arr2);// 4 5 6
    //     if(s2.has(item)){
    //         return true;
    //     }else{
    //         return false;
    //     }
    // });
    // let result = [...new Set(arr)].filter(item => new Set(arr2).has(item));
    // console.log(result);
    //3. 并集
    // let union = [...new Set([...arr, ...arr2])];
    // console.log(union);
    //4. 差集
    let diff = [...new Set(arr)].filter(item => !(new Set(arr2).has(item)));
    console.log(diff);
</script>

Map

ES6 提供了 Map 数据结构。

它类似于对象,也是键值对的集合。

但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。

Map也实现了iterator接口,所以可以使用『扩展运算符』和『 for…of…』进行遍历。

Map的属性和方法:
  1) size 返回 Map的元素个数
  2) set 增加一个新元素,返回当前 Map
  3) get 返回键名对象的键值
  4) has 检测 Map中是否包含某个元素,返回 boolean值
  5) clear 清空集合,返回 undefined

<script>
    //声明 Map
    let m = new Map();
    //添加元素
    m.set('name','少林达摩院');
    m.set('change', function(){
        console.log("我们可以改变你!!");
    });
    let key = {
        school : 'SHAOLING'
    };
    m.set(key, ['北京','上海','深圳']);
    //size
    // console.log(m.size);
    //删除
    // m.delete('name');
    //获取
    // console.log(m.get('change'));
    // console.log(m.get(key));
    //清空
    // m.clear();
    //遍历
    for(let v of m){
        console.log(v);
    }
    // console.log(m);
</script>

class类

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。

通过 class关键字,可以定义类。

基本上, ES6 的 class可以看作只是一个语法糖,它的绝大部分功能, ES5 都可以做到,新的 class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

知识点:
  1) class声明类
  2) constructor定义构造函数初始化
  3) extends继承父类
  4) super调用父级构造方法
  5) static定义静态方法和属性
  6) 父类方法可以重写

类声明

<script>
    //手机
    function Phone(brand, price){
        this.brand = brand;
        this.price = price;
    }
    //添加方法
    Phone.prototype.call = function(){
        console.log("我可以打电话!!");
    }
    //实例化对象
    let Huawei = new Phone('华为', 5999);
    Huawei.call();
    console.log(Huawei);

    //class
    class Shouji{
        //构造方法 名字不能修改
        constructor(brand, price){
            this.brand = brand;
            this.price = price;
        }
        //方法必须使用该语法, 不能使用 ES5 的对象完整形式
        call(){
            console.log("我可以打电话!!");
        }
    }
    let onePlus = new Shouji("1+", 1999);
    console.log(onePlus);
</script>

静态成员

<script>
    // function Phone(){
    // }
    // Phone.name = '手机';
    // Phone.change = function(){
    //     console.log("我可以改变世界");
    // }
    // Phone.prototype.size = '5.5inch';
    // let nokia = new Phone();
    // console.log(nokia.name);
    // // nokia.change();
    // console.log(nokia.size);

    class Phone {
        //静态属性
        static name = '手机';
        static change() {
            console.log("我可以改变世界");
        }
    }
    let nokia = new Phone();
    console.log(nokia.name); // undefined
    console.log(Phone.name); // 手机
</script>

构造函数继承及对父类方法重写

<script>
    class Phone {
        //构造方法
        constructor(brand, price) {
                this.brand = brand;
                this.price = price;
        }
        //父类的成员属性
        call() {
            console.log("我可以打电话!!");
        }
    }

    class SmartPhone extends Phone {
        //构造方法
        constructor(brand, price, color, size) {
            super(brand, price); // Phone.call(this, brand, price)
            this.color = color;
            this.size = size;
        }
        photo() {
            console.log("拍照");
        }
        playGame() {
            console.log("玩游戏");
        }
        call() {
            console.log('我可以进行视频通话');
        }
    }

    const xiaomi = new SmartPhone('小米', 799, '黑色', '4.7inch');
    console.log(xiaomi);
    xiaomi.call();
    xiaomi.photo();
    xiaomi.playGame();
</script>

class的set-get

<script>
    // get 和 set  
    class Phone{
        get price(){
            console.log("价格属性被读取了");
            return 'iloveyou';
        }
        set price(newVal){
            console.log('价格属性被修改了');
        }
    }

    //实例化对象
    let s = new Phone();
    // console.log(s.price);
</script>

数值扩展

1.Number.EPSILON 是 JavaScript 表示的最小精度

2.二进制和八进制:ES6 提供了二进制和八进制数值的新的写法,分别用前缀 0b和 0o表示。

3.Number.isFinite  检测一个数值是否为有限数

4.Number.isNaN 检测一个数值是否为 NaN

5.Number.parseInt Number.parseFloat字符串转整数

6.Number.isInteger 判断一个数是否为整数

7.Math.trunc 将数字的小数部分抹掉

8.Math.sign 判断一个数到底为正数 负数 还是零
<script>
    //0. Number.EPSILON 是 JavaScript 表示的最小精度
    //EPSILON 属性的值接近于 2.2204460492503130808472633361816E-16
    // function equal(a, b){
    //     if(Math.abs(a-b) < Number.EPSILON){
    //         return true;
    //     }else{
    //         return false;
    //     }
    // }
    // console.log(0.1 + 0.2 === 0.3);
    // console.log(equal(0.1 + 0.2, 0.3))

    //1. 二进制和八进制
    // let b = 0b1010;
    // let o = 0o777;
    // let d = 100;
    // let x = 0xff;
    // console.log(x);

    //2. Number.isFinite  检测一个数值是否为有限数
    // console.log(Number.isFinite(100));
    // console.log(Number.isFinite(100/0));
    // console.log(Number.isFinite(Infinity));
    
    //3. Number.isNaN 检测一个数值是否为 NaN 
    // console.log(Number.isNaN(123)); 

    //4. Number.parseInt Number.parseFloat字符串转整数
    // console.log(Number.parseInt('5211314love'));
    // console.log(Number.parseFloat('3.1415926神奇'));

    //5. Number.isInteger 判断一个数是否为整数
    // console.log(Number.isInteger(5));
    // console.log(Number.isInteger(2.5));

    //6. Math.trunc 将数字的小数部分抹掉  
    // console.log(Math.trunc(3.5));

    //7. Math.sign 判断一个数到底为正数 负数 还是零
    console.log(Math.sign(100));
    console.log(Math.sign(0));
    console.log(Math.sign(-20000));
</script>

对象方法扩展

ES6新增了一些 Object对象的方法
  1) Object.is 比较两个值是否严格相等,与『 ===』行为基本一致(+0 与 NaN)
  2) Object.assign 对象的合并,将源对象的所有可枚举属性,复制到目标对象
  3) __proto__、 setPrototypeOf、 setPrototypeOf可以直接设置对象的原型

<script>
    //1. Object.is 判断两个值是否完全相等 
    // console.log(Object.is(120, 120));// === 
    // console.log(Object.is(NaN, NaN));// === 
    // console.log(NaN === NaN);// === 

    //2. Object.assign 对象的合并
    // const config1 = {
    //     host: 'localhost',
    //     port: 3306,
    //     name: 'root',
    //     pass: 'root',
    //     test: 'test'
    // };
    // const config2 = {
    //     host: 'http://atguigu.com',
    //     port: 33060,
    //     name: 'atguigu.com',
    //     pass: 'iloveyou',
    //     test2: 'test2'
    // }
    // console.log(Object.assign(config1, config2));

    //3. Object.setPrototypeOf 设置原型对象  Object.getPrototypeof
    const school = {
        name: '少林达摩院'
    }
    const cities = {
        xiaoqu: ['北京','上海','深圳']
    }
    Object.setPrototypeOf(school, cities);
    console.log(Object.getPrototypeOf(school));
    console.log(school);    
</script>

 模块化

模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。

模块化的优势:
  1) 防止命名冲突
  2) 代码复用
  3) 高维护性

ES6之前的模块化规范有
  1) CommonJS => NodeJS、 Browserify
  2) AMD => requireJS
  3) CMD => seaJS

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

m1.js

//分别暴露
export let school = '少林达摩院';
export function teach() {
    console.log("我们可以教给你绝世神功");
}

m2.js

//统一暴露
let school = '少林达摩院';
function findJob(){
    console.log("我们可以帮助你学会化缘!!");
}
export {school, findJob};

m3.js

//默认暴露
export default {
    school: '少林达摩院',
    change: function(){
        console.log("我们可以改变你!!");
    }
}

app.js

//入口文件

//模块引入
import * as m1 from "./m1.js";
import * as m2 from "./m2.js";
import * as m3 from "./m3.js";

// console.log(m1);
// console.log(m2);
// console.log(m3);

// m1.teach();
// m2.findJob();
// m3.default.change();

//修改背景颜色为粉色
import $ from 'jquery';// const $ = require("jquery");
$('body').css('background','pink');

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ES6 模块化</title>
</head>
<body>
    <script type="module">
        //1. 通用的导入方式
        //引入 m1.js 模块内容
        // import * as m1 from "./src/js/m1.js";
        // //引入 m2.js 模块内容
        // import * as m2 from "./src/js/m2.js";
        // //引入 m3.js 
        // import * as m3 from "./src/js/m3.js";

        //2. 解构赋值形式
        // import {school, teach} from "./src/js/m1.js";
        // import {school as guigu, findJob} from "./src/js/m2.js";
        // import {default as m3} from "./src/js/m3.js";

        //3. 简便形式  针对默认暴露
        // import m3 from "./src/js/m3.js";
        // console.log(m3);
    </script>

    <script src="./src/js/app.js" type="module"></script>
</body>
</html>

 

posted @ 2022-04-21 11:06  城南以南123  阅读(64)  评论(0编辑  收藏  举报