JavaScript ES6

50 阅读20分钟

let 声明变量

  1. 变量不能重复声明
  2. 块级作用域
  3. for 循环中用let定义i

const定义常量

  • 必须要赋初始值

  • 首字母一般大写

  • 常量值不能修改

  • 块级作用域

  • 对于数组和对象的元素修改,不算做对常量的修改,不会报错

    const Team = ['1','2','3']
    Team.push('4') // 不报错
    Team = 100 //报错
    

解构赋值

  1. 数组的解构

    const arr = ['one','two','three']
    let [1,2,3] = arr
    console.log(1) //one
    console.log(2) //two
    console.log(3) //three 
    
  2. 对象的解构

    const john = {
         name: '1',
         age : '2',
         sayHello: function(){
             console.log('hello')
         }
    }
    let { n, a, s} = john
    console.log(n)
    console.log(a)
    console.log(s)
    s() //hello
    //如果单用 必须同名
    let {sayHello} = john
    sayHello()
    

模版字符串

  1. 声明

    let str = `模版字符串`
    console.log(str,typeof str) //string
    
  2. 内容中可以直接出现换行符

    let str = `<ul>
               <li>1</li>
               <li>2</li>
               </ul>`
    
  3. 变量拼接

    let lovest = '魏翔'
    let out = `${lovest}是我心中最搞笑的演员`
    console.lg(out)
    

对象的简写

let name = 'ma'
let sayHello = function(){
    console.log('hello')
}
const jony = {
    name,
    sayhello,
    improve(){
        console.log('hello hello')
    }
}
//等效于
const jony = {
    name: name,
    sayhello: sayhello,
    improve(){
        console.log('hello hello')
    }
}

箭头函数

  1. 声明

    let fn = function(a,b){
        return a+b
    }
    fn(1,2)
    //简化
    let fn = (a,b) =>{
        return a+b
    }
    fn(1,2)
    
  2. 箭头函数中this是静态的,this始终指向函数声明时所在作用域下的this的值

    function getName(){
        console.log(this.name)
    }
    let getName2 =()=>{
        console.log(this.name)
    }
    let name = '小翔'
    const matx = {
        name: 'xiaoxiang'
    }
    //直接调用
    getName() //小翔
    getName2() //小翔
    //call方法调用
    getName.call(matx)//this指向matx xiaoxiang
    getName2.call(matx)//this不会指向matx 小翔
    
  3. 不能作为构造实例化对象

    let Person = (name,age)=>{
        this.name = name
        this.age = age
    }
    let me = new Person('mtx',20) //错误使用
    console.log(me)//报错
    
  4. 不能使用 agruments 变量

    let fn =()=>{
        console.log(arguments)
    }
    fn(1,2,3) //arguments is not defined
    
  5. 箭头函数的简写

    1. 省略小括号 当形参有且只有一个的时候

      let add = n =>{
          return n + n
      }
      console.log(add(9))
      
    2. 省略花括号{ } 当代码体只有一条语句时 return关键字必须省略 语句执行结果就是函数返回值

      let pow = (n) =>n*n
      console.log(pow(8))
      
  6. 运用

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>箭头函数实践</title>
        <style>
            div {
                width: 200px;
                height: 200px;
                background: #58a;
            }
        </style>
    </head>
    <body>
        <div id="ad"></div>
        <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);
        </script>
    </body>
    </html>
    

    总结:

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

函数参数的默认值设置

  1. 形参初始值 具有默认值的参数,一般位置要靠后(潜规则)
function add(a,b,c=10){
    return a + b + c
}
console.log(add(1,2))
  1. 与解构赋值结合

    function connect(option){
        let host = options.host
        let username = options.username
        let password = options.password
    } //这种方式繁琐
    connect({
         host: 'localhost',
         username: 'root',
         password: 'root',
    })
    //利用解构赋值操作 还可以赋初始值
    function connect({host,username,password,port='3006'})
    

rest操作符...

用于获取函数的实参,用来代替 arguments

//ES5获取实参
function date(){
    console.log(arguments)
}
date('2022','02','04')//返回arguments数组 arguments[0]='2022'

//rest 参数
function date(...args){
     console.log(args)//返回一个数组 可以使用一些方法 filter some every map
}
date('2022','02','04')

//rest 参数必须要放到参数最后
function fn(a,b,...args){
    console.log(a)
    console.log(b)
    console.log(args)
}
fn(1,2,3,4,5,6) //a=1 b=2 args=[3,4,5,6]

扩展(spread)运算符...

能将[数组]转换为逗号分隔的[参数序列]

const arr=[1,2,3]
function nums(){
    console.log(arguments)
}
nums(...arr)

区分rest操作符和扩展运算符

//当被用于迭代器中时,它是一个扩展操作符:
function foo(x,y,z) {
  console.log(x,y,z);
}
let arr = [1,2,3];
foo(...arr); // 1 2 3
//当被用于函数传参时,是一个rest操作符
function foo(...args) {
  console.log(args);
}
foo( 1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]

运用:

//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

数据类型 Symbol 表示独一无二的值

  1. 特点

    • Symbol的值是唯一的,用来解决命名冲突的问题
    • Symbol的值不能与其他数据进行运算
    • Symbol定义的对象属性不能使用for...in循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名
  2. 基础

      //创建Symbol
        let s = Symbol();
        // console.log(s, typeof s); //symbol
        let s2 = Symbol('尚硅谷');
        let s3 = Symbol('尚硅谷');
        console.log(s2 === s3) //false 
        //Symbol.for 创建   Symbol()函数 Symbol.for对象
        let s4 = Symbol.for('尚硅谷');
        let s5 = Symbol.for('尚硅谷');
        console.log(s4 === s5) //true
    
     //不能与其他数据进行运算
         let result = s + 100; //错误
         let result = s > 100; //错误
         let result = s + s; //错误
    
     // USONB 七种数据类型
          u  undefine  //未定义
          s  string  Symbol  //字符串 Symbol
          o  object  //对象
          n  null number  
          b  boolean
    
  3. 使用

    let game = {
                name:'俄罗斯方块',
                up: function(){},
                down: function(){}
        };
    //现在需要向game对象中添加方法 up和down 但是不确定game对象是否已经有了up 和down属性名
    //先声明对象
    let methods = {
       up: Symbol(),
       down: Symbol()
     }
     //向game对象添加 up 和 down 方法
     game[methods.up] = function(){
         console.log('up')
     }
    game[methods.down] = function(){
         console.log('down')
    }
    console.log(game) //有两个symbol类型的方法
    
    //或者:
     let youxi = {
                name:"狼人杀",
                say: function(){
                    console.log('say')
                },
                [Symbol('say')]: function(){
                    console.log("我可以发言")
                },
                zibao: function(){
                    console.log('zibao')
                },
                [Symbol('zibao')]: function(){
                    console.log('我可以自爆');
                }
            }
    
       console.log(youxi)
    

    image-20220204104625019

  4. Symbol内置属性

    //1 Symbol.hasInstance 当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法
    class Person{
                static [Symbol.hasInstance](param){
                    console.log(param);
                    console.log("我被用来检测类型了");
                    return false; //设置为true则返回true 可以自己设置
                }
            }
      let o = {name:'o'};
      console.log(o instanceof Person); //{name: 'o'} 我被用来检测类型了 false
    
    //2 Symbol.isConcatSpreadable 
    //对象的Symbol.isConcatSpreadable属性等于的是一个布尔值,表示该对象用于 Array.prototype.concat()时,是否可以展开。
      const arr = [1,2,3];
      const arr2 = [4,5,6];
      console.log(arr.concat(arr2)); //[1,2,3,4,5,6]
      arr2[Symbol.isConcatSpreadable] = false;
      console.log(arr.concat(arr2)); //[1,2,3,[4,5,6]]
    

迭代器

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

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

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

    1. Array
    2. Arguments
    3. Set
    4. Map
    5. String
    6. TypedArray
    7. NodeList
    const arr = ['1','2','3']
    //for...of 遍历
    for(let k of arr){
         console.log(k)
    }
    
    let iterator = xiyou[Symbol.iterator]()
    
    //调用对象的next方法
    console.log(iterator.next())// 1
    console.log(iterator.next())// 2
    console.log(iterator.next())// 3
    console.log(iterator.next())// undefined
    
  3. 自定义遍历数据

    //声明一个对象
            const banji = {
                name: "终极一班",
                stus: [
                    'xiaoming',
                    'xiaoning',
                    'xiaotian',
                    'knight'
                ],
                //定义 iterator接口 否则无法使用 for...of 遍历
                [Symbol.iterator]() {
                    //索引变量
                    let index = 0;
                    //把当前this对象保存到_this
                    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};
                            }
                        }
                    };
                }
            }
    
            //遍历这个对象的stus属性
            for (let v of banji) {
                console.log(v);
            }
    

生成器函数 异步编程

之前学过的 node中的fs ajax 都是纯回调函数方式

//参考JS高级笔记 Generator生成器
function* foo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
}
var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}
var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }

image-20220204142119869

回调地狱:

image-20220204143002012

解决办法:

 // 1s 后控制台输出 111  2s后输出 222  3s后输出 333 
        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();
            },3000)
        }

        function * gen(){
            yield one();
            yield two();
            yield three();
        }

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

实例:

//模拟获取  先获取用户数据  再获取用户对应订单数据  再获取对应商品数据 
        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)//用户数据 next传入data='用户数据'
            let orders = yield getOrders();
            console.log(orders)//订单数据 next传入data=...
            let goods = yield getGoods();
            console.log(goods) //商品数据 next传入data=...
        }

        //调用生成器函数
        let iterator = gen();
        iterator.next();//只需要调用一次next()函数就可以把三个函数依次执行 因为三个函数内部又执行next()

Promise

Promise是 ES6引入的异步编程的新解决方案 。语法上 Promise是一个构造函数, 用来封装异步操作并可以获取其成功或失败的结果。

  • resolve() 中可以放置一个参数用于向下一个 then 传递一个值,then 中的函数也可以返回一个值传递给 then。但是,如果 then 中返回的是一个 Promise 对象,那么下一个 then 将相当于对这个返回的 Promise 进行操作,这一点从刚才的计时器的例子中可以看出来。

  • reject() 参数中一般会传递一个异常给之后的 catch 函数用于处理异常。

  • 但是请注意以下两点:

    • resolve 和 reject 的作用域只有起始函数,不包括 then 以及其他序列;

    • resolve 和 reject 并不能够使起始函数停止运行,别忘了 return。

  • Promise 类有 .then() .catch() 和 .finally() 三个方法,这三个方法的参数都是一个函数,.then() 可以将参数中的函数添加到当前 Promise 的正常执行序列,.catch() 则是设定 Promise 的异常处理序列,.finally() 是在 Promise 执行的最后一定会执行的序列。 .then() 传入的函数会按顺序依次执行,有任何异常都会直接跳到 catch 序列

  1. 基础

    //实例化 Promise 对象
    const p = new Promise(function(resolve,reject){
        setTimeout(function(){
            let data = '数据库中的用户数据'
            //成功 执行reslove 返回data p状态为成功
            resolve(data)
            //失败 执行reject 返回err p状态为失败
            //let err = '数据读取失败'
            //reject(err)
        },1000)
    })
    //调用 promise 对象的 then 方法  resolve()中可以放置一个参数用于向下一个then 传递一个值
    p.then(function(value){
        //成功调用
        console.log(value) //数据库中的用户数据
    },function(reason){
        //失败调用
        console.log(reason) //数据读取失败
    })
    
  2. 封装读取文件

    //1. 引入 fs 模块
    const fs = require('fs');
    
    //2. 调用方法读取文件
    // fs.readFile('./resources/为学.md', (err, data)=>{
    //     //如果失败, 则抛出错误
    //     if(err) throw err;
    //     //如果没有出错, 则输出内容
    //     console.log(data.toString());
    // });
    
    //3. 使用 Promise 封装
    const p = new Promise(function(resolve, reject){
        fs.readFile("./demo.txt", (err, data)=>{
            //判断如果失败
            if(err) reject(err);
            //如果成功
            resolve(data);
        });
    });
    
    p.then(function(value){
        console.log(value.toString());
    }, function(reason){
        console.log("读取失败!!");
    });
    
    
  3. 封装AJax请求

    <script>
            // 接口地址: 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);
            });
        </script>
    
  4. then方法

     //创建 promise 对象
            const p = new Promise((resolve, reject)=>{
                setTimeout(()=>{
                    resolve('用户数据');
                    reject('出错啦');
                }, 1000)
            });
    
            //调用 then 方法 then方法的返回结果是Promise对象, 对象状态由回调函数的执行结果决定
            //如果回调函数中返回的结果是非promise类型的属性, 状态为成功, 返回值为对象的成功的值
    
            const result = p.then(value => {
                console.log(value);
                //1. 非 promise 类型的属性 失败
                 return 'iloveyou';
                //2. 是 promise 对象 成功
                return new Promise((resolve, reject)=>{
                  resolve('ok');
                //也可以设置 reject('error'); 那么设置为失败状态
                // });
                //3. 抛出错误  失败
                // throw new Error('出错啦!');
                throw '出错啦!';
            }, reason=>{
                console.warn(reason);
            });
    
            //链式调用
            p.then(value=>{
                //成功回调
            },reason=>{
                //失败回调
            }).then(value=>{
                //成功回调
            },reason=>{
                //失败回调
            })
            console.log(result)
                
            //只保留一个调用
            p.then(value=>{
                //成功调用
            }).then(value=>{
                //成功调用
            })
    
  5. 实践 读取多个文件

    //引入 fs 模块
    const fs = require("fs");
    
    // fs.readFile('./resources/为学.md', (err, data1)=>{
    //     fs.readFile('./resources/插秧诗.md', (err, data2)=>{
    //         fs.readFile('./resources/观书有感.md', (err, data3)=>{
    //             let result = data1 + '\r\n' +data2  +'\r\n'+ data3;
    //             console.log(result);
    //         });
    //     });
    // });
    
    //使用 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'));
    });
    
  6. catch 用来指定promise失败的回调

    catch和then的区别:  如果在then的第一个函数里抛出了异常,后面的catch能捕获到,而then的第二个函数捕获不到。

     const p = new Promise((resolve, reject)=>{
                setTimeout(()=>{
                    //设置 p 对象的状态为失败, 并设置失败的值
                    reject("出错啦!");
                }, 1000)
            });
        
            //第一种方式
            // p.then(function(value){}, function(reason){
            //     console.error(reason);
            // });
    
            //catch 方式
            p.catch(function(reason){
                console.warn(reason);
            });
    

Set

ES6 提供了新的数据结构 Set(集合 )。它类似于数组,但成员的值都是唯 一的 ,集合实现了 iterator接口,所以可以使用『扩展运算符』和『 for…of…』进 行遍历。

//声明
let s = new Set()
console.log(s,typeof s)//Set(0)  object
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);
        }

实践:

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];
        ///[...new Set(arr)]  把集合转换为数组形式
        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);

重点:[...new Set(arr)] 把集合转换为数组形式

Map

ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。 但是“键” 的范围不限于字符串,各种类型的值(包括对象)都可以当作键。 Map也实现了 iterator接口,所以可以使用『扩展运算符』和『 for…of…』进行遍历

//声明 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);
        }

        // console.log(m);

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

静态成员

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

        let nokia = new Phone();

        console.log(nokia.name);//undefined
        // nokia.change()  //change is not a function 不互通
        console.log(nokia.size); //5.5inch

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

        let nokia = new Phone();
        console.log(nokia.name);//手机
        console.log(Phone.name);//我可以改变世界

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

类继承

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

        class SmartPhone extends Phone {
            //构造方法
            constructor(brand, price, color, size){
                //父类的constructor方法
                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();

getter和setter的设置

// get 和 set  
        class Phone{
            get price(){
                console.log("价格属性被读取了");
                return '111';
            }
            //set 必须要有一个参数
            set price(newVal){
                console.log('价格属性被修改了');
            }
        }

        //实例化对象
        let s = new Phone();
        console.log(s.price);//价格属性被读取了
        s.price = 'free';//价格属性被修改了

数值拓展

       0. Number.EPSILONJavaScript 表示的最小精度
        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;//二进制 0b
        let o = 0o777;//八进制 0o
        let d = 100;//十进制 100
        let x = 0xff;//十六进制 0x
        console.log(x);

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

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

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

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

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

对象方法拓展

        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'
        }
        //Object.assign(被覆盖对象,覆盖对象)
        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. ES6模块化语法

    • export命令用于规定模块的对外接口

      1. 分别暴露

        export let m1 = 1
        export let m2 = 2
        
      2. 统一暴露

        export {
           m1: 1,
           m2: 2
           nums: function(){
               console.log('3')
           }
        }
        
      3. 默认暴露

        export default{
            school: 'ATGUIGU'
            change: function(){
                consloe.log('hello')
            }
        }
        
    • import命令用于输入其他模块提供的功能

      1. 通用方式导入

        import * as m1 from './m1.js' //所有的数据都导入
        
      2. 解构赋值形式(export name)

        import {school,teach}from './m2.js'
        //对于默认暴露
        import {default as m3}from './m3.js'
        
      3. 简介形式 只针对默认暴露(export default)

        import m3 from './m3.js'
        

babel对ES6模块化代码转换

 1. 安装工具 npm i babel-cli babel-preset-env browserify(webpack) -D
 2. 编译 npx babel src/js -d dist/js --presets=babel-preset-env
 3. 打包 npx browserify dist/js/app.js -o dist/bundle.js

image-20220204215347234

ES7新特性

 // includes 返回布尔值   indexOf 返回下标(索引)
     const mingzhu = ['西游记','红楼梦','三国演义','水浒传'];

        //判断
     console.log(mingzhu.includes('西游记'));//true
     console.log(mingzhu.includes('金瓶梅'));/

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

ES8 async与await

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

  1. async

    • async 函数返回值为 promise 对象
    • promise 对象的结果由 async 函数执行的返回值决定
    • 如果在函数中 return 一个直接量,async 会把这个直接量通过 Promise.resolve() 封装成 Promise 对象。
    //async 函数
            async function fn(){
                // 返回一个字符串
                // return '尚硅谷';
                // 1 返回的结果不是一个 Promise 类型的对象, 返回的结果就是成功 Promise 对象          
                // return;  也是成功的 Promise 对象
                
                //2 抛出错误, 返回的结果是一个失败的 Promise
                // throw new Error('出错啦!');
                
                //3 返回的结果如果是一个 Promise 对象
                return new Promise((resolve, reject)=>{
                    resolve('成功的数据');
                    //reject("失败的错误");也可以设置为失败
                });
            }
    
            const result = fn();
    
            //调用 then 方法
            result.then(value => {
                console.log(value);//成功的数据
            }, reason => {
                console.warn(reason);//失败的数据
            })
    
  2. await

    • await 必须写在 async 函数中,但 async 函数可以没有 await
    • await 右侧表达式一般为 promise 对象,若是一个值则返回该值
    • async函数必须等到内部所有的 await 命令的 Promise 对象执行完,才会发生状态改变。
    • await 表达式会暂停当前 async function 的执行,阻塞下边的代码,等待 Promise 处理完成。若 Promise 正常处理(fulfilled),其回调的resolve函数参数作为 await 表达式的值,继续执行 async function。
    • 若 Promise 处理异常(rejected),await 表达式会把 Promise 的异常原因抛出,而不是返回,需要用try catch捕获。
    • 即await 返回的是promise 成功的值,若是promise失败,await就会抛出该异常,需要通过try catch捕获
    • await相当于Promise的then并且同一作用域下await下面的内容全部作为then中回调的内容
     //创建 promise 对象
            const p = new Promise((resolve, reject) => {
                // resolve("用户数据");
                reject("失败啦!");
            })
    
            // await 要放在 async 函数中.
            async function main() {
                try {
                    let result = await p;
                    console.log(result);//若p被设置成功 输出:用户数据
                } catch (e) {
                    console.log(e);//若p被设置失败 输出:失败啦
                }
            }
            //调用函数
            main();
    
  3. 多个await之间的执行顺序

    • await运算符后面不是一个promise对象而是一个值,那await表达式的运算结果就是该值
    • await会先执行await后面的表达式,等到的是一个promise对象,然后阻塞后面的代码,执行同步任务,等着promise对象resolve,然后得到resolve的值,作为await表达式的运算结果
    function test1() {
    	console.log(111);
    }
    function test2() {
    	console.log(222);
    }
    async function test3() {
    	await test1();
    	await test2();
    }
    test3();
    //输出结果111 222
    
    function test1() {
    	setTimeout( ()=> {
    		console.log(111);
    	}, 1000);
    }
    function test2() {
    	console.log(222);
    }
    async function test3() {
    	await test1();
    	await test2();
    }
    test3();
    //输出结果222 111
    
    function test1() {
    	return new Promise(resolve => {
            setTimeout(() => {
                console.log(111);
                resolve();
            }, 2000)
        })
    }
    function test2(res) {
    	setTimeout(() => {
    	    console.log(222);
    	}, 1000);
    }
    async function test3() {
        await test1();
        await test2();
    }
    test3();
    //输出结果: 111 222
    

    执行test3(),执行test1(),遇到promise,阻塞代码,此时test2()不执行,运行test1(),等待2s,输出111,运行resolve(),执行test2(),等待1s,输出222。

  4. 测试

     function testSometing() {
            console.log("testSomething");
            return "return testSomething";
        }
        
        async function testAsync() {
            console.log("testAsync");
            return Promise.resolve("hello async");
        }
        
        async function test() {
            console.log("test start...");
        
            const testFn1 = await testSometing();
            console.log('testFn1 '+testFn1);
        
            const testFn2 = await testAsync();
            console.log('testFn2 '+testFn2);
        
            console.log('test end...');
        }
        
        test();
        
        var promiseFn = new Promise((resolve)=> { 
                            console.log("promise START...");
                            resolve("promise RESOLVE");
                        });
        promiseFn.then((val)=> console.log('val '+val));
        
        console.log("===END===")
    

    image-20220209185500355

    1. test()打印出”test start...“
    2. await testSomething(),根据”await后面对应的的函数会先执行一遍,然后就会跳出整个async函数来执行后面js栈的代码“,会先执行testSometing()这个函数,打印出“testSometing”的字符串。
    3. 然后跳出async,执行promiseFn打印出“promise START...”,返回的Promiseresolve("promise RESOLVE")放入Promise队列
    4. 继续执行主线程栈中的console.log("===END==="),打印“===END===”
    5. 根据”等本轮事件循环执行完了之后又会跳回到async函数中等待await后面表达式的返回值,如果返回值为非promise则继续执行async函数后面的代码,否则将返回的promise放入Promise队列“,跳回async函数,因为testSometing() 不是async函数返回值为非promise打印"return testSomething"
    6. test()函数继续执行,执行到testFn2(),执行testAsync(),打印"testAsync"
    7. 然后跳出async,因为主线程没有任务,所以进入Promise队列,执行promiseFn.then((val)=> console.log(val));打印出“promise RESOLVE”
    8. 跳回到test()继续执行console.log(testFn2)的返回值,打印出“hello async”
    9. 最后打印“test end...”
  5. asyncawait结合读取文件

    //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(){
        //获取为学内容  await 返回的是 promise 成功的值
        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();
    
  6. async和await封装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");
                //再次测试 发送第二个AJAX请求
                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();
    

ES8对象扩展方法

        //声明对象
        const school = {
            name:"尚硅谷",
            cities:['北京','上海','深圳'],
            xueke: ['前端','Java','大数据','运维']
        };

        //获取对象所有的键名
        console.log(Object.keys(school));
        //获取对象所有的值
        console.log(Object.values(school));
         //entries 返回一个二维数组 每个成员是一个数组 该数组第一个元素是键 第二个元素是值
        console.log(Object.entries(school));
        //可以用来创建 Map
        const m = new Map(Object.entries(school));
        console.log(m.get('cities'));

        //对象属性的描述对象
        //原型对象
console.log(Object.getOwnPropertyDescriptors(school));
        //描述对象
        const obj = Object.create(null, {
            name: {
                //设置值
                value: '尚硅谷',
                //属性特性
                writable: true,
                configurable: true,
                enumerable: true
            } 
        });

ES9扩展运算符与rest参数

ES6只针对数组,不能用于对象。

在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',
            password: 'root',
            type: 'master'
        });


        //对象合并
        const skillOne = {
            q: '天音波'
        }

        const skillTwo = {
            w: '金钟罩'
        }

        const skillThree = {
            e: '天雷破'
        }
        const skillFour = {
            r: '猛龙摆尾'
        }

        const mangseng = {...skillOne, ...skillTwo, ...skillThree, ...skillFour};
        console.log(mangseng)

ES9正则扩展

  1. 命名捕获分组

            //声明一个字符串
            let str = '<a href="http://www.atguigu.com">尚硅谷</a>';
            //提取 url 与 『标签文本』
            const reg = /<a href="(.*)">(.*)<\/a>/;
            //执行
            const result = reg.exec(str);
            console.log(result);
            // console.log(result[1]);
            // console.log(result[2]);
    
            
            let str = '<a href="http://www.atguigu.com">尚硅谷</a>';
            //分组命名
            const reg = /<a href="(?<url>.*)">(?<text>.*)<\/a>/;
             //分组命名为url text
            const result = reg.exec(str);
            console.log(result.groups.url);
            console.log(result.groups.text);
    

    image-20220205083258502

  2. 反向断言

     //声明字符串
            let str = 'JS5211314你知道么555啦啦啦';
            //正向断言 根据后面内容判断前面的内容是否合法
            const reg = /\d+(?=啦)/;//\d查找数字 n+ 匹配任何包含至少一个n的字符串
            const result = reg.exec(str);//555
    
            //反向断言 根据前面的内容判断后面的内容是否合法
            const reg = /(?<=么)\d+/;
            const result = reg.exec(str);
            console.log(result);
    
  3. dotAll模式

    dotAll属性表明是否在正则表达式中一起使用"s"修饰符(引入/s修饰符,使得.可以匹配任意单个字符

     let str = `
            <ul>
                <li>
                    <a>肖生克的救赎</a>
                    <p>上映日期: 1994-09-10</p>
                </li>
                <li>
                    <a>阿甘正传</a>
                    <p>上映日期: 1994-07-06</p>
                </li>
            </ul>`;
            //声明正则
            // const reg = /<li>\s+<a>(.*?)<\/a>\s+<p>(.*?)<\/p>/; \s查找空白字符
           //利用dotAll模式  /s .就可以匹配任意字符 
            const reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/gs; /g全局匹配
            //执行匹配
            // 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

  1. 对象扩展方法 Object.fromEntries 将二维数组转换为对象
        //二维数组
        // const result = Object.fromEntries([
        //     ['name','尚硅谷'],
        //     ['xueke', 'Java,大数据,前端,云计算']
        // ]);

        //Map
        // const m = new Map();
        // m.set('name','ATGUIGU');
        // const result = Object.fromEntries(m);

        //ES8 Object.entries 也可以将对象转为二维数组
        const arr = Object.entries({
            name: "尚硅谷"
        })
        console.log(arr);

​ 2.字符串方法扩展

       // ES5 trim 清除字符串两侧空白字符
        let str = '   iloveyou   ';
        console.log(str);
        console.log(str.trimStart());//清除左侧空白字符
        console.log(str.trimEnd());//清除右侧空白字符

3.数组方法拓展

        //flat 
        //将多维数组转化为低维数组
        const arr = [1,2,3,4,[5,6]];
        console.log(arr.flat()) //二维转一维
        const arr = [1,2,3,4,[5,6,[7,8,9]]];
        //参数为维度 是一个数字
        console.log(arr.flat(2));//三维转二维

        //flatMap
        const arr = [1,2,3,4];
        const result = arr.map(item => item * 10);//[10 20 30 40]
        const result = arr.map(item => [item * 10]);//返回二维数组[[10],[20],[30],[40]]
        const result = arr.flatMap(item => [item * 10]);//降维 [10 20 30 40]
        console.log(result);

4.Symbol.prototype.description

        //创建 Symbol
        let s = Symbol('尚硅谷');

        console.log(s.description);

ES11

  1. 私有属性

     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() //可以访问
    
  2. Promise.allSettled

    //声明两个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]);//返回的PromiseStatus都是成功的  即时p2设置为reject
            
            //与 allsettled 类似
            const res = Promise.all([p1, p2]);
            //结果由p1 p2 决定 有一个失败res状态就是失败两个都成功才会成功
    
            console.log(res);
    

    p1 p2 都成功 res:

    image-20220205094107464

  3. String.prototype.matchAll 用来得到正则批量匹配的结果

    let str = `<ul>
                <li>
                    <a>肖生克的救赎</a>
                    <p>上映日期: 1994-09-10</p>
                </li>
                <li>
                    <a>阿甘正传</a>
                    <p>上映日期: 1994-07-06</p>
                </li>
            </ul>`;
    
            //声明正则
            const reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/sg
    
            //调用方法
            const result = str.matchAll(reg);
    
            // for(let v of result){
            //     console.log(v);
            // }
    
            const arr = [...result];
            console.log(arr);
    
  4. 可选链操作符

     // ?.
            function main(config){
              //原来方式 const dbHost = config && config.db && config.db.host;判断config是否存在再判断config.db是否存在再取出host值
                
                const dbHost = config?.db?.host;
                console.log(dbHost);//即使不存在也不会报错 输出undefined
            }
    
            main({
                db: {
                    host:'192.168.1.100',
                    username: 'root'
                },
                cache: {
                    host: '192.168.1.200',
                    username:'admin'
                }
            })
    
  5. 动态import 实现按需加载

    image-20220205095902409

  6. BigInt 类型

            //大整形
            let n = 521n;
            console.log(n, typeof(n));//521n BigInt
    
            //函数
            let n = 123;
            console.log(BigInt(n));//123n
            console.log(BigInt(1.2));//报错 不是整数
    
            //用于大数值运算
            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)) 
    
  7. 绝对全局对象globalThis

    • 始终指向全局对象

    • 浏览器下指向window

    • node.js环境下指向global