• ES6 ~ ES11 学习笔记


    ·课程地址

    ES6

    let

    let 不能重复声明变量(var 可以)

    let a;
    let b, c, d;
    let e = 100;
    let f = 521, g = "atguigu", h = [];
    
    • 1
    • 2
    • 3
    • 4

    let 具有块级作用域,内层变量外层无法访问

    let 不存在变量提升(运行前收集变量和函数,提前声明),但是 var 存在变量提升:

    console.log(song);	// undefined
    var song = "hello";
    
    • 1
    • 2

    不影响作用域链:

    {
        let school = "atguigu";
        function fn() {
            console.log(school);	// atguigu
        }
        fn();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    案例:

    <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++) {    // var 是不行的
                items[i].onclick = function () {
                    //修改当前元素的背景颜色
                    // this.style.background = 'pink';
                    items[i].style.background = 'pink';
                }
            }
        script>
    body>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    如果在 for 循环中使用了 var 声明 i,那么它会被提升到全局作用域 window.i,回调函数调用时它的值一直是 3

    const

    1. 一定要赋初始值
    2. 一般常量使用大写(潜规则)
    3. 常量的值不能修改
    4. 常量同样拥有块儿级作用域
    5. 对于数组和对象的元素修改,不算做对常量的修改,不会报错(地址不变)
    const TEAM = ['UZI','MXLG','Ming','Letme'];
    TEAM.push('Meiko');
    
    • 1
    • 2

    解构赋值

    数组解构:

    const F4 = ['小沈阳','刘能','赵四','宋小宝'];
    let [xiao, liu, zhao, song] = F4;	// 解构赋值
    console.log(xiao);
    console.log(liu);
    console.log(zhao);
    console.log(song);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    对象解构:

    const zhao = {
        name: '赵本山',
        age: '不详',
        xiaopin: function(){
            console.log("我可以演小品");
        }
    };
    
    let {name, age, xiaopin} = zhao;	// 对象解构
    console.log(name);
    console.log(age);
    console.log(xiaopin);
    xiaopin();
    
    let {xiaopin} = zhao;
    xiaopin();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    let {xiaopin} = zhao;
    xiaopin();
    
    • 1
    • 2

    模板字符串

    模板字符串内容中可以直接出现换行符

    let str = `
    • 沈腾
    • 玛丽
    • 魏翔
    • 艾伦
    `
    ;
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    变量拼接:

    let lovest = '魏翔';
    let out = `${lovest}是我心目中最搞笑的演员!!`;
    console.log(out);
    
    • 1
    • 2
    • 3

    对象简化写法

    用于属性名和变量值相同的情况

    let name = '尚硅谷';
    let change = function () {
        console.log('我们可以改变你!!');
    }
    
    const school = {
        name,
        change,
        improve() {
            console.log("我们可以提高你的技能");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    箭头函数

    let fn = (a, b) => {
        return a + b;
    }
    
    • 1
    • 2
    • 3

    注意事项:箭头函数的 this 是静态的。this 始终指向函数声明时所在作用域下的 this 的值。不能使用 call 切换 this 指向

    function getName() {
        console.log(this.name);
    }
    let getName2 = () => {
        console.log(this.name);
    }
    
    //设置 window 对象的 name 属性
    window.name = '尚硅谷';
    const school = {
        name: "ATGUIGU"
    }
    
    getName();   // window.name = '尚硅谷';
    getName2();  // window.name = '尚硅谷';
    
    getName.call(school);    // name: "ATGUIGU"
    getName2.call(school);   // window.name = '尚硅谷';
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 不能作为构造实例化对象
    • 不能使用 arguments 变量
    • 当代码体只有一条语句的时候, 此时 return 必须省略,语句的执行结果就是函数的返回值
    let double = n => n + n;
    
    • 1

    案例:点击 div 后等待 2s 后变色

    传统写法:

    let ad = document.getElementById('ad');
    ad.addEventListener("click", function () {
        let _this = this;
        setTimeout(function() {
            _this.style.background = 'pink';
        }, 2000);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    使用箭头函数的写法:

    let ad = document.getElementById('ad');
    ad.addEventListener("click", function () {
        setTimeout(() => {
            this.style.background = 'pink';
        }, 2000);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    案例 2:从数组返回偶数元素

    const arr = [1,6,9,10,100,25];
    const result = arr.filter(item => item % 2 === 0);
    
    • 1
    • 2

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

    函数默认参数

    ES6 允许给函数参数赋初始值,一般位置要靠后

    function add(a, b, c = 10) {
        return a + b + c;
    }
    
    • 1
    • 2
    • 3

    与解构赋值结合:

    function connect({ host = "127.0.0.1", username, password, port }) {
        console.log(host)
        console.log(username)
        console.log(password)
        console.log(port)
    }
    connect({
        host: 'atguigu.com',
        username: 'root',
        password: 'root',
        port: 3306
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    rest 参数

    类似于 arguments,用于支持函数的不定参数

    ES5 写法:

    function date() {
        console.log(arguments);
    }
    date('白芷', '阿娇', '思慧');
    
    • 1
    • 2
    • 3
    • 4

    使用 rest 参数:

    function date(...args) {	// 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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    扩展运算符

    数组解构:

    const arr = [1, 2, 3];
    function fn() {
    	console.log(arguments);
    }
    fn(...arr);		// fn(1, 2, 3);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    数组拼接:

    const kuaizi = ['王太利', '肖央'];
    const fenghuang = ['曾毅', '玲花'];
    // const zuixuanxiaopingguo = kuaizi.concat(fenghuang);
    const zuixuanxiaopingguo = [...kuaizi, ...fenghuang];
    
    • 1
    • 2
    • 3
    • 4

    数组拷贝:

    const sanzhihua = ['E', 'G', 'M'];
    const sanyecao = [...sanzhihua];//  ['E','G','M']
    console.log(sanyecao);
    
    • 1
    • 2
    • 3

    将伪数组转化为真正的数组:

    const divs = document.querySelectorAll('div');
    const divArr = [...divs];
    console.log(divArr);	// arguments
    
    • 1
    • 2
    • 3

    Symbol

    ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是 JS 的第七种数据类型,是一种类似于字符串的数据类型

    Symbol 的特点:

    • 值唯一,用来解决命名冲突的问题
    • 不能与其他数据进行运算
    • Symbol 定义的对象属性不能用 for in 循环遍历,但可以使用 Reflect.ownkeys 来获取对象的所有键名
    //创建Symbol
    let s = Symbol();
    // console.log(s, typeof s);
    let s2 = Symbol('尚硅谷');
    let s3 = Symbol('尚硅谷');
    //Symbol.for 创建
    let s4 = Symbol.for('尚硅谷');
    let s5 = Symbol.for('尚硅谷');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    JS 七大数据类型

    USONB you are so niubility

    • u undefined
    • s string symbol
    • o object
    • n null number
    • b boolean

    Symbol 的作用:给对象添加独一无二的属性或方法

    let youxi = {
        name:"狼人杀",
        [Symbol('say')]: function(){
            console.log("我可以发言")
        },
        [Symbol('zibao')]: function(){
            console.log('我可以自爆');
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    Iterator

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

    获取数组的迭代器:

    let it = arr[Symbol.iterator]();
    
    • 1
    const xiyou = ['唐僧','孙悟空','猪八戒','沙僧'];
    
    // 使用 for...of 遍历数组
    for(let v of xiyou){
        console.log(v);
    }
    
    let iterator = xiyou[Symbol.iterator]();
    
    //调用对象的next方法
    console.log(iterator.next());
    console.log(iterator.next());
    console.log(iterator.next());
    console.log(iterator.next());
    console.log(iterator.next());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    自定义迭代器:返回一个对象,其具有 next() 方法

    const banji = {
        name: "终极一班",
        stus: [
            'xiaoming',
            'xiaoning',
            'xiaotian',
            'knight'
        ],
        [Symbol.iterator]() {
            //索引变量
            let index = 0;
            //
            let _this = this;
            return {
                next: function () {
                    if (index < _this.stus.length) {
                        const result = { value: _this.stus[index], done: false };
                        //下标自增
                        index++;
                        //返回结果
                        return result;
                    }else{
                        return {value: undefined, done: true};
                    }
                }
            };
        }
    }
    
    //遍历这个对象 
    for (let v of banji) {
        console.log(v);
    }
    
    • 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

    生成器

    生成器函数是 ES6 提供的一种异步编程解决方案

    function * gen(){
        // console.log(111);
        yield '一只没有耳朵';
        // console.log(222);
        yield '一只没有尾部';
        // console.log(333);
        yield '真奇怪';
        // console.log(444);
    }
    
    let iterator = gen();
    console.log(iterator.next());	// {value: v, done: false}
    console.log(iterator.next());
    console.log(iterator.next());
    console.log(iterator.next());
    
    //遍历
    for(let v of gen()){	// 一只没有……
        console.log(v);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    生成器函数参数

    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());   // "AAA", {value: 111, done: false}
    //next方法可以传入实参
    console.log(iterator.next('BBB'));  // one
    console.log(iterator.next('CCC'));  // two
    console.log(iterator.next('DDD'));  // three
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    生成器函数实例:使用定时器,1s 后控制台输出 111 2s后输出 222 3s后输出 333

    setTimeout(() => {
        console.log(111);
        setTimeout(() => {
            console.log(222);
            setTimeout(() => {
                console.log(333);
            }, 3000);
        }, 2000);
    }, 1000);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    function one(){
        setTimeout(()=>{
            console.log(111);
            iterator.next();	// 调用下一个定时器
        },1000)
    }
    
    function two(){
        setTimeout(()=>{
            console.log(222);
            iterator.next();	// 调用下一个定时器
        },2000)
    }
    
    function three(){
        setTimeout(()=>{
            console.log(333);
            iterator.next();	// 调用下一个定时器:{value: undefined, done: true}
        },3000)
    }
    
    function * gen(){
        yield one();
        yield two();
        yield three();
    }
    
    //调用生成器函数
    let iterator = gen();
    iterator.next();
    
    • 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

    生成器函数实例2:

    //模拟获取  用户数据  订单数据  商品数据 
    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();
        let orders = yield getOrders();
        let goods = yield getGoods();
    }
    
    //调用生成器函数
    let iterator = gen();
    iterator.next();
    
    • 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

    Promise

    Promise 用于异步编程,语法上 Promise 是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果

    //实例化 Promise 对象
    const p = new Promise(function (resolve, reject) {
        setTimeout(function () {
            //
            // let data = '数据库中的用户数据';
            // resolve
            // resolve(data);
    
            let err = '数据读取失败';
            reject(err);
        }, 1000);
    });
    
    //调用 promise 对象的 then 方法
    p.then(function (value) {
        console.log(value);
    }, function (reason) {
        console.error(reason);
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    Promise 读取文件案例:

    const fs = require('fs');
    
    const p = new Promise(function(resolve, reject) {
        fs.readFile("path", (err, data) => {
            if (err) reject(err);
            else resolve(data);
        });
    });
    
    p.then(
        value => console.log(value.toString()),
        reason => console.warn("error!!")
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Promise 封装 AJAX:

    // 接口地址: https://api.apiopen.top/getJoke
    const p = new Promise((resolve, reject) => {
        //1. 创建对象
        const xhr = new XMLHttpRequest();
    
        //2. 初始化
        xhr.open("GET", "https://api.apiopen.top/getJ");
    
        //3. 发送
        xhr.send();
    
        //4. 绑定事件, 处理响应结果
        xhr.onreadystatechange = function () {
            //判断
            if (xhr.readyState === 4) {
                //判断响应状态码 200-299
                if (xhr.status >= 200 && xhr.status < 300) {
                    //表示成功
                    resolve(xhr.response);
                } else {
                    //如果失败
                    reject(xhr.status);
                }
            }
        }
    })
    
    //指定回调
    p.then(function (value) {
        console.log(value);
    }, function (reason) {
        console.error(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
    • 30
    • 31
    • 32
    • 33

    调用 then 方法 then方法的返回结果是 Promise 对象,对象状态由回调函数的执行结果决定

    如果回调函数中返回的结果是 非 promise 类型的属性,状态为成功,返回值为对象的成功的值:

    const result = p.then(value => {
        console.log(value);
        //2. 非 promise 类型的属性
        return 'iloveyou';
    }, reason => {
        console.warn(reason);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    如果返回值是 Promise 类型的对象,则直接返回:

    const result = p.then(value => {
        console.log(value);
        //3. 是 promise 对象
        return new Promise((resolve, reject)=>{
            // resolve('ok');
        	  reject('error');
        });
    }, reason => {
        console.warn(reason);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    如果抛出错误,则返回一个 reject 的 Promise:

    const result = p.then(value => {
        console.log(value);
        throw '出错啦!';
    }, reason => {
        console.warn(reason);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Promise 读取多个文件:

    const p = new Promise((resolve, reject) => {
        fs.readFile("./resources/为学.md", (err, data) => {
            resolve(data);
        });
    });
    
    p.then(value => {
        return new Promise((resolve, reject) => {
            fs.readFile("./resources/插秧诗.md", (err, data) => {
                resolve([value, data]);
            });
        });
    }).then(value => {
        return new Promise((resolve, reject) => {
            fs.readFile("./resources/观书有感.md", (err, data) => {
                //压入
                value.push(data);
                resolve(value);
            });
        })
    }).then(value => {
        console.log(value.join('\r\n'));
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    catch:指定 Promise 失败回调

    const p = new Promise((resolve, reject)=>{
        setTimeout(()=>{
            //设置 p 对象的状态为失败, 并设置失败的值
            reject("出错啦!");
        }, 1000)
    });
    
    // p.then(function(value){}, function(reason){
    //     console.error(reason);
    // });
    
    p.catch(function(reason){
        console.warn(reason);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    Set

    //声明一个 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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    Set 实践:

    去重:

    let arr = [1,2,3,4,5,4,3,2,1];
    //1. 数组去重
    let result = [...new Set(arr)];
    
    • 1
    • 2
    • 3

    交集:

    let arr2 = [4,5,6,5,6];
    let result = [...new Set(arr)].filter(item => new Set(arr2).has(item));
    
    • 1
    • 2

    并集:

    let union = [...new Set([...arr, ...arr2])];
    
    • 1

    差集:

    let diff = [...new Set(arr)].filter(item => !(new Set(arr2).has(item)));
    
    • 1

    Map

    //声明 Map
    let m = new Map();
    
    //添加元素
    m.set('name', '尚硅谷');
    m.set('change', function () {
        console.log("我们可以改变你!!");
    });
    let key = {
        school: 'ATGUIGU'
    };
    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);
    }
    
    • 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

    class

    //手机
    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);
    
    • 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

    静态成员:

    class Phone {
    	static name = "shouji";
    	static change() {
    		console.log("i can change the world");
    	}
    }
    
    conosle.log(Phone.name);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    类继承:ES5 实现

    function Phone(brand, price){
        this.brand = brand;
        this.price = price;
    }
    
    Phone.prototype.call = function(){
        console.log("我可以打电话");
    }
    
    //智能手机
    function SmartPhone(brand, price, color, size){
        Phone.call(this, brand, price); // this bind to Phone
        this.color = color;
        this.size = size;
    }
    
    //设置子级构造函数的原型
    SmartPhone.prototype = new Phone;
    SmartPhone.prototype.constructor = SmartPhone;
    
    //声明子类的方法
    SmartPhone.prototype.photo = function(){
        console.log("我可以拍照")
    }
    
    SmartPhone.prototype.playGame = function(){
        console.log("我可以玩游戏");
    }
    
    const chuizi = new SmartPhone('锤子',2499,'黑色','5.5inch');
    
    console.log(chuizi);
    
    • 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

    类继承:ES6 实现

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

    get 和 set

    // get 和 set  
    class Phone {
        get price() {
            console.log("价格属性被读取了");
            return 'iloveyou';
        }
    
        set price(newVal) {  // 必须有形参
            console.log('价格属性被修改了');
        }
    }
    
    //实例化对象
    let s = new Phone();
    
    // console.log(s.price);
    s.price = 'free';
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    数值扩展

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

    对象方法扩展

    //1. Object.is 判断两个值是否完全相等 
    console.log(Object.is(120, 120));   // true
    console.log(Object.is(NaN, NaN));   // true
    console.log(NaN === NaN);   // false
    
    //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'
    }
    // config2 覆盖了 config1
    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);
    
    • 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

    模块化

    分别暴露:

    // m1.js
    export let school = '尚硅谷';
    
    export function teach() {
        console.log("我们可以教给你开发技能");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    导入:

    import * as m1 from "./src/js/m1.js";
    
    • 1

    统一暴露:

    let school = '尚硅谷';
    
    function findJob(){
        console.log("我们可以帮助你找工作!!");
    }
    
    export {school, findJob};
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    默认暴露:

    export default {
        school: 'ATGUIGU',
        change: function(){
            console.log("我们可以改变你!!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    引入模块:

    //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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在入口文件统一引入:

    // app.js
    //模块引入
    import * as m1 from "./m1.js";
    import * as m2 from "./m2.js";
    import * as m3 from "./m3.js";
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Babel

    Babel 是一个 JavaScript 编译器,将高版本的 ES 代码编译为 ES5

    安装打包工具:

    npm i babel-cli babel-preset-env browserify -D
    
    • 1

    编译:

    npx babel src/js -d dist/js --presets=babel-preset-env
    
    • 1

    打包:

    npx browserify dist/js/app.js -o dist/bundle.js
    
    • 1

    模块化引入 NPM 包:

    在入口文件导入 jquery

    import $ from 'jquery';// const $ = require("jquery");
    $('body').css('background','pink');
    
    • 1
    • 2

    重新编译并打包

    ES7

    Array.prototype.includes

    检测数组中是否包含某个元素

    指数操作符

    console.log(2 ** 10);   // 1024
    console.log(Math.pow(2, 10));
    
    • 1
    • 2

    ES8

    async 和 await

    async 和 await 两种语法结合可以让异步代码像同步代码一样

    async 函数

    • async 函数的返回值是 promise 对象
    • promise 对象的结果由 async 函数执行的返回值决定
    //async 函数
    async function fn() {
        // return '尚硅谷';
        // 返回的结果不是一个 Promise 类型的对象, 返回的结果就是成功 Promise 对象
        // return;
        //抛出错误, 返回的结果是一个失败的 Promise
        // throw new Error('出错啦!');
        //返回的结果如果是一个 Promise 对象
        return new Promise((resolve, reject) => {
            resolve('成功的数据');
            // reject("失败的错误");
        });
    }
    
    const result = fn();
    
    //调用 then 方法
    result.then(value => {
        console.log(value);
    }, reason => {
        console.warn(reason);
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    await 函数

    • await 必须写在 async 函数中
    • await 右侧的表达式一般为 promise 对象
    • await 返回的是 promise 成功的值
    • await 的 promise 失败了,就会抛出异常,需要通过 try catch 处理
    //创建 promise 对象
    const p = new Promise((resolve, reject) => {
        // resolve("用户数据");
        reject("失败啦!");
    })
    
    // await 要放在 async 函数中.
    async function main() {
        try {
            let result = await p;
            //
            console.log(result);
        } catch (e) {
            console.log(e);
        }
    }
    //调用函数
    main();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    案例:async 和 await 读取文件内容

    //1. 引入 fs 模块
    const fs = require("fs");
    
    //读取『为学』
    function readWeiXue() {
        return new Promise((resolve, reject) => {
            fs.readFile("./resources/为学.md", (err, data) => {
                //如果失败
                if (err) reject(err);
                //如果成功
                resolve(data);
            })
        })
    }
    
    function readChaYangShi() {
        return new Promise((resolve, reject) => {
            fs.readFile("./resources/插秧诗.md", (err, data) => {
                //如果失败
                if (err) reject(err);
                //如果成功
                resolve(data);
            })
        })
    }
    
    function readGuanShu() {
        return new Promise((resolve, reject) => {
            fs.readFile("./resources/观书有感.md", (err, data) => {
                //如果失败
                if (err) reject(err);
                //如果成功
                resolve(data);
            })
        })
    }
    
    //声明一个 async 函数
    async function main(){
        //获取为学内容
        let weixue = await readWeiXue();
        //获取插秧诗内容
        let chayang = await readChaYangShi();
        // 获取观书有感
        let guanshu = await readGuanShu();
    
        console.log(weixue.toString());
        console.log(chayang.toString());
        console.log(guanshu.toString());
    }
    
    main();
    
    • 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

    案例2:await 和 async 发送 ajax 请求

    // 发送 AJAX 请求, 返回的结果是 Promise 对象
    function sendAJAX(url) {
        return new Promise((resolve, reject) => {
            //1. 创建对象
            const x = new XMLHttpRequest();
    
            //2. 初始化
            x.open('GET', url);
    
            //3. 发送
            x.send();
    
            //4. 事件绑定
            x.onreadystatechange = function () {
                if (x.readyState === 4) {
                    if (x.status >= 200 && x.status < 300) {
                        //成功啦
                        resolve(x.response);
                    } else {
                        //如果失败
                        reject(x.status);
                    }
                }
            }
        })
    }
    
    //promise then 方法测试
    // sendAJAX("https://api.apiopen.top/getJoke").then(value=>{
    //     console.log(value);
    // }, reason=>{})
    
    // async 与 await 测试  axios
    async function main() {
        //发送 AJAX 请求
        let result = await sendAJAX("https://api.apiopen.top/getJoke");
        //再次测试
        let tianqi = await sendAJAX('https://www.tianqiapi.com/api/?version=v1&city=%E5%8C%97%E4%BA%AC&appid=23941491&appsecret=TXoD5e8P')
    
        console.log(tianqi);
    }
    
    main();
    
    • 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

    Object.values 和 Object.entries

    //声明对象
    const school = {
        name: "尚硅谷",
        cities: ['北京', '上海', '深圳'],
        xueke: ['前端', 'Java', '大数据', '运维']
    };
    
    //获取对象所有的键
    console.log(Object.keys(school));   // [ 'name', 'cities', 'xueke' ]
    //获取对象所有的值
    console.log(Object.values(school)); //[ '尚硅谷', [ '北京', '上海', '深圳' ], [ '前端', 'Java', '大数据', '运维' ] ]
    //entries
    console.log(Object.entries(school));    // [[k, v]]
    //创建 Map
    const m = new Map(Object.entries(school));
    console.log(m.get('cities'));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    Object.getOwnPropertyDescriptors

    //对象属性的描述对象
    console.log(Object.getOwnPropertyDescriptors(school));
    
    const obj = Object.create(null, {
        name: {
            //设置值
            value: '尚硅谷',
            //属性特性
            writable: true,
            configurable: true,
            enumerable: true
        }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    ES9

    Rest 参数与扩展运算符

    // rest 参数
    function connect({ host, port, ...user }) {
        console.log(host);
        console.log(port);
        console.log(user);
    }
    
    connect({
        host: '127.0.0.1',
        port: 3306,
        username: 'root',   // user.username
        password: 'root',   // user.password
        type: 'master'      // user.type
    });
    
    
    // 对象合并
    const skillOne = {
        q: '天音波'
    }
    
    const skillTwo = {
        w: '金钟罩'
    }
    
    const skillThree = {
        e: '天雷破'
    }
    const skillFour = {
        r: '猛龙摆尾'
    }
    
    const mangseng = { ...skillOne, ...skillTwo, ...skillThree, ...skillFour };
    
    console.log(mangseng)
    
    • 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

    正则

    命名捕获分组

    传统的使用位置捕获分组:

    //声明一个字符串
    let str = '尚硅谷';
    
    // //提取 url 与 『标签文本』
    const reg = /(.*)<\/a>/;
    
    // //执行
    const result = reg.exec(str);
    
    console.log(result);
    console.log(result[1]);
    console.log(result[2]);
    

    使用命名捕获分组:

    let str = '尚硅谷';
    //分组命名
    const reg = /(?.*)<\/a>/;
    
    const result = reg.exec(str);
    
    console.log(result.groups.url);
    
    console.log(result.groups.text);
    

    反向断言

    正向断言:

    //声明字符串
    let str = 'JS5211314你知道么555啦啦啦';
    //正向断言
    const reg = /\d+(?=啦)/;
    const result = reg.exec(str);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    反向断言:

    //反向断言
    const reg = /(?<=么)\d+/;
    const result = reg.exec(str);
    console.log(result);
    
    • 1
    • 2
    • 3
    • 4

    dotAll 模式

    js 中的 . 是一个元字符,它能匹配除换行符以外的任意单个字符

    dotAll 模式就是在模式后面增加模式修饰符 s,令 . 能够匹配换行符和空格(任意字符)。相当于用 \s+ 替换了 .*?

    //dot  .  元字符  除换行符以外的任意单个字符
    let str = `
            `;
    //声明正则
    // const reg = /
  • \s+(.*?)<\/a>\s+

    (.*?)<\/p>/; // no dotall const reg = /

  • .*?(.*?)<\/a>.*?

    (.*?)<\/p>/gs; // use dotall //执行匹配 // const result = reg.exec(str); let result; let data = []; while (result = reg.exec(str)) { data.push({ title: result[1], time: result[2] }); } //输出结果 console.log(data);

  • ES10

    Object.fromEntries

    从二维数组或 Map 中构造对象

    const result = Object.fromEntries([
        ['name','尚硅谷'],
        ['xueke', 'Java,大数据,前端,云计算']
    ]);
    
    • 1
    • 2
    • 3
    • 4
    const m = new Map();
    m.set('name','ATGUIGU');
    const result = Object.fromEntries(m);
    
    • 1
    • 2
    • 3
    const arr = Object.entries({
        name: "尚硅谷"
    })
    console.log(arr);
    
    • 1
    • 2
    • 3
    • 4

    Object.entries()Object.fromEntries() 互为逆运算

    // trim
    let str = '   iloveyou   ';
    
    console.log(str);
    console.log(str.trimStart());
    console.log(str.trimEnd());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    flat 与 flatMap

    用于展平数组,其接受一个参数,指明展平数组的维度

    //flat 平
    //将多维数组转化为低位数组
    // const arr = [1,2,3,4,[5,6]];
    const arr = [1, 2, 3, 4, [5, 6, [7, 8, 9]]];
    //参数为深度 是一个数字
    console.log(arr.flat(2));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    //flatMap
    const arr = [1, 2, 3, 4];
    const result = arr.flatMap(item => [item * 10]);
    console.log(result);
    
    • 1
    • 2
    • 3
    • 4

    Symbol.prototype.description

    获取创建 Symbol 时的字符串:

    //创建 Symbol
    let s = Symbol('尚硅谷');
    console.log(s.description);
    
    • 1
    • 2
    • 3

    ES11

    私有属性

    # 开头

    class Person{
        //公有属性
        name;
        //私有属性
        #age;
        #weight;
        //构造方法
        constructor(name, age, weight){
            this.name = name;
            this.#age = age;
            this.#weight = weight;
        }
    
        intro(){
            console.log(this.name);
            console.log(this.#age);
            console.log(this.#weight);
        }
    }
    
    //实例化
    const girl = new Person('晓红', 18, '45kg');
    
    console.log(girl.name);
    // console.log(girl.#age);
    // console.log(girl.#weight);
    
    girl.intro();
    
    • 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

    Promise.allSettled

    接受一个 promise 数组,返回一个 promise 对象。对象中包含了全部结束的 promise

    //声明两个promise对象
    const p1 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('商品数据 - 1');
        }, 1000)
    });
    
    const p2 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('商品数据 - 2');
            // reject('出错啦!');
        }, 1000)
    });
    
    //调用 allsettled 方法
    // const result = Promise.allSettled([p1, p2]); // 返回全部结束的 promise
    
    // const res = Promise.all([p1, p2]);   // 全部成功才会返回成功的 promise,任意一个失败都会返回失败的 promise
    
    console.log(res);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    String.prototype.matchAll

    用于得到正则批量匹配的结果

    let str = ``;
    
    //声明正则
    const reg = /
  • .*?(.*?)<\/a>.*?

    (.*?)<\/p>/sg //调用方法 const result = str.matchAll(reg); for(let v of result){ console.log(v); } const arr = [...result]; console.log(arr);

  • 可选链操作符

    避免了层层判断一个对象是否具有某属性

    // ?.
    function main(config) {
        // const dbHost = config && config.db && config.db.host;
        const dbHost = config?.db?.host;
    
        console.log(dbHost);
    }
    
    main({
        db: {
            host: '192.168.1.100',
            username: 'root'
        },
        cache: {
            host: '192.168.1.200',
            username: 'admin'
        }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    动态 import

    用于动态地从文件中加载模块,用时加载,提高了运行效率

    import 返回的是一个 promise 对象

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>动态 import title>
    head>
    <body>
        <button id="btn">点击button>
        <script src="./js/app.js" type="module">script>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    // app.js
    // import * as m1 from "./hello.js";
    //获取元素
    const btn = document.getElementById('btn');
    
    btn.onclick = function(){
        import('./hello.js').then(module => {
            module.hello();
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    // hello.js
    export function hello(){
        alert('Hello');
    }
    
    • 1
    • 2
    • 3
    • 4

    bigint

    在数字后面加 n

    let n = 521n;
    console.log(n, typeof(n));
    
    • 1
    • 2

    BigInt 用于将一个数字构造为 BigInt

    let n = 123;
    console.log(BigInt(n));
    
    • 1
    • 2

    BigInt 可以用于大数运算

    let max = Number.MAX_SAFE_INTEGER;
    console.log(max);
    console.log(max + 1);
    console.log(max + 2);
    
    console.log(BigInt(max))
    console.log(BigInt(max) + BigInt(1))
    console.log(BigInt(max) + BigInt(2))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    globalThis

    浏览器下指向 window;nodejs 下指向 global

  • 相关阅读:
    Python基础入门篇【18】--python中的流程控制之条件判断
    新鲜出炉的跨域问题:前端设置credential: ‘include‘,后端不能用‘*‘通配符匹配所有源
    vue-cropper在ie11下选择本地图片后,无显示、拒绝访问的问题
    【数据结构】包装类&简单认识泛型
    前端代码规范神器之f2elint的使用教程
    【软考笔记】(二)程序设计语言
    golang jwt(hs,es,rs,ed)密钥生成、加签验签案例
    mysql数据库安装
    基于小波变换的EMG信号病人数据matlab仿真分析
    快速上手Shell,看这一篇就够了
  • 原文地址:https://blog.csdn.net/DanielSYC/article/details/135999855