Spiga

前端提升2:ES

2025-03-08 15:14:23

一、let与const

  • let用来声明变量,它的用法类似于 var
  • 不存在变量提升
  • 同一个作用域内不能重复定义同一个名称
  • 有着严格的作用域
  • const 声明一个只读的常量。一旦声明,常量的值就不能改变。
    • 保持let的不存在变量提升,同一个作用域内不能重复定义同一个名称,有着严格的作用域
    • const 实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动
window.onload = function () {
    // let 和var的不同
    // 1、不存在变量提升
    // console.log(a);
    // var a = 100;
    // //解析过程
    // //预解析
    // var a;
    // //逐行解析
    // console.log(a);
    // a = 100;

    // //
    // console.log(a);  //Cannot access 'a' before initialization
    // let a = 100;

    //2、同一个作用域内不能重复定义同一个名称
    // var a = 10;
    // var a = 100;
    // console.log(a); //100

    // var a = 10;
    // let a = 100;
    // console.log(a);  //Identifier 'a' has already been declared

    // let a = 10;
    // let a = 100;

    // let a = 10;
    // a = 100;
    // console.log(a); //100

    //3、有着严格的作用域
    // function fn(){
    //     var a = 'a';
    //     if(true){  //块级作用域
    //         let a = 'b'
    //     };
    //     console.log(a) //'a'
    // };
    // fn();

    //4、块级作用域的重要性
    // for(var i=0;i<5;i++){};
    // console.log(i); //5  i成为全局变量


    // for(let i=0;i<5;i++){};
    // console.log(i); // i is not defined

    // //扩展题
    // let arr = [];
    // for(let i=0;i<5;i++){
    //     arr[i] = function(){
    //         console.log(i)
    //     }
    // };
    // arr[2](); //2
    // arr[1](); //1

    // //提升题
    // function f1(id){  ////2、同一个作用域内不能重复定义同一个名称
    //     //var id = 100;
    //     let id = 88;
    //     console.log(id);  //Identifier 'id' has already been declared
    // };
    // f1(100);

    // var x = 1;
    // function f2(){
    //     if(true){
    //         x = 2;  
    //         console.log(x);  // Cannot access 'x' before initialization
    //         let x;   ////2、同一个作用域内不能重复定义同一个名称
    //     }
    // };
    // f2();


    // let x = y ,y = 100;  //Cannot access 'y' before initialization
    // function f3(){
    //     console.log(x,y)
    // };
    // f3();



    // const 的定义
    // const 声明一个只读的常量。一旦声明,常量的值就不能改变。
    // 保持let的不存在变量提升,同一个作用域内不能重复定义同一个名称,有着严格的作用域;
    // const x = 10;  //常量  只读
    // x = 100; //error
    // console.log(x); //Assignment to constant variable.

    // const y; // Missing initializer in const declaration  const 声明变量时,需要立即初始化,不能留到以后赋值
    // console.log(y);

    //变量是引用类型
    const arr = [1,2,3,4];
    arr[0] = 100;
    //console.log(arr);  //[ 100, 2, 3, 4 ]
    arr.push(5); //ok
    arr = ['a','b'];  //Assignment to constant variable.


    const obj = {id:1};
    obj['name'] = 'amy';
    console.log(obj)  //{ id: 1, name: 'amy' }
    obj = {v:1}; //error
}

二、解构

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)

window.onload = function () {
    // 什么是解构
    // ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
    // 解构是ES6的新特性, 比ES5代码简洁,清晰,减少代码量
    //数组解构
    // let a = 1;
    // let b = 2;
    // let c = 3;
    // let d = 4;

    //简化
    // let a = 1,b=2,c=3,d=4;

    //es6
    //let [a,b,c,d] = [1,2,3,4];

    // let color = ['red','blue','green'];
    // let [r,b,g] = color;

    //匹配不成功
    // let [a,b,c,d] = [1,2,3,4,5,6];
    // 左边模式匹配,定义变量  ,右边对应的数据

    // let [a,b,c,d] = [[1,2,3],true,{id:1},100];

    // let [x,[y1,y2],z] = [1,[2,3],4]
    // console.log(y1)//2

    let arr = [100,200];
    // if(arr[0]){
    //     var x = arr[0]
    // }else {
    //     var x = 1;
    // };
    // //简化
    // var x = arr[0] || 1;
    // //默认值
    // let [x=1] = arr;

    let [x=1,y=1] = arr;
    console.log(x,y)  //100 200
    // let x = arr[0] ||1;
    // let y = arr[1] ||1;
    //默认值什么生效?成员等于undefined,默认值才会生效

    //扩展题
    var [a=1,b=2] = [10]; //10,2
    var [a,b='y'] = ['a']; //'a','y'
    var [a=1,b] = [100,null]; //100,null
    var [a=1,b] = [undefined,null]; //1,null

    //默认值是表达式,惰性求值,只有在用到的时候,才会求值
    function f(){
        return 88
    };
    var [a = 1,b = f()] = [100,200];  //100 200  
    console.log(b) //200

    var b = 200 || f();  //惰性求值

    var [a = 1,b = f()]  = []; //1,88
    var [a = 1,b = f()]  = [undefined];  //1,88

    //特殊的情况
    var [a = 1,b = a] =[];
    console.log(a,b)  //1 1
    var [a = 1,b = a] =[2,3]; //2 3
    var [a = 1,b = a] =[2]; 
    console.log(a,b) //2 2

    var [a = b,b = 1] =[]; 
    var [a = b,b = 1] =[2]; 
    
    
    // 对象解构
    //let [a,b,c] = [1,2,3];
    //let {name,id} = {id:1,name:'jack'};

    let obj1 = {
        age:18,
        value:'v1',
        name:'jack',
        msg:'12345'
    };
    // let $id = obj1.id;
    // let _age = obj1.age;
    // let v = obj.value;

    let {value:v="v",age:_age,id:$id=1} = obj1;
    console.log($id)  //1

    //别名  && 默认值
    //扩展题:
    var {x:a = 10} = {};
    console.log(x); //x is not defined
    console.log(a);//ok

    var {x:a,y:b = 5} = {x:1,y:null};
    console.log(a,b) //a=1;b = null

    var {x:a=2,y:b = 5} = {x:undefined,y:null};
    console.log(a,b) //a=2;b = null


    //常见对象解构的写法
    // import {ref} from 'vue';  //import obj from 'vue'   obj.ref; obj.a
    // import React,{useState} from 'vue';

    // const fn = function({count,num}){
    //     return count+num
    // };
    // const fn = ({count,num}) =>count+num;
    // let obj2 = {count:100,num:200};
    // fn(obj2)

    //
    let obj3 = {
        id:1,
        msg:'error',
        arr:[5,6,7,8],
        o:{v:1}
    };
    //求obj3对象中id和arr的和 1+5+6+7+8
    // function fn({id,arr}){
    //     let i = id;
    //     for(var v of arr){
    //         i+=v
    //     };
    //     return i
    // };
    // fn(obj3)
    //reduce()
    const sum = ({id,arr})=>arr.reduce((total,cur)=>total+cur,id);
    console.log(sum(obj3));  //27

    // let {o:{v:value=1}} = obj3;
    // console.log(value) //1

    // axios.get().then(res=>{
    //     let {data:{result:a=1},code,msg} = res;
    //     console.log(a)
    // })
    
    
    // 函数参数解构
    // function sum([x,y]){
    //     return x+y
    // };
    // var arr = [10,20];
    // sum(arr)
    

    function sum([x=1,y=1]=[]){
        console.log(x+y)
    };
    var arr = [10,20];
    sum()


    //对象
    function sum2({x=0,y=0}={}){
       return [x,y]
    };
    //箭头函数
    //let sum2 = ({x=0,y=0}={})=>[x,y];
    const o = {x:10,y:20};
    sum2(o); // [10,20]   相当于 {x=0,y=0} = {x:10,y:20} || {}
    sum2({x:10}); //[10,0]   相当于 {x=0,y=0} = {x:10} || {}
    sum2({});  //[0,0]
    sum2();    //[0,0]

     //另一种写法
     let sum3 = ({x,y}={x:0,y:0})=>[x,y];  //相当于 {x,y} = {x:10,y:20} || {x:0,y:0}
     const o3 = {x:10,y:20};
     sum3(o3); //    [10,20]
     sum3({x:10}); //    [10, undefined]  相当于 {x,y} = {x:10} || {x:0,y:0}
     sum3({});  //  [undefined,undefined]   相当于 {x,y} = {} || {x:0,y:0}
     sum3();    //  [0,0]    相当于 {x,y} =  {x:0,y:0}
}

三、新的扩展

1. 字符串

window.onload = function () {
    //字符串常用方法
    // charAt();  //返回指定索引位置的字符  
    // indexOf();//返回字符串中检索指定字符第一次出现的位置 
    // lastIndexOf();//返回字符串中检索指定字符最后一次出现的位置  
    // slice()  // 提取字符串的片断
    // split()  //把字符串分割为子字符串数组  
    // toLowerCase()// 把字符串转换为小写
    // toUpperCase() //把字符串转换为大写    
    // substr()// 从起始索引号提取字符串中指定数目的字符
    // substring()  //提取字符串中两个指定的索引号之间的字符

    //ES6 扩展
    // includes():     返回布尔值,表示是否找到了参数字符串。
    //模糊匹配
    'hello'.indexOf('h')!=-1;
    'hello'.includes('h');
    const color = ['red','orange','blue','green'];
    const filter = color.filter(function(item){
        return item.includes('r')
        //return item.indexOf('n')!=-1;
    });
    //简化:const filter = color.filter(item=>item.includes('n'));
    console.log(filter);

    let str = 'hello world!';
    // startsWith():   返回布尔值,表示参数字符串是否在原字符串的头部。
    str.startsWith('h'); //true
    str.startsWith('h',3);  //第二个参数表示搜索的位置 从0

    // endsWith():     返回布尔值,表示参数字符串是否在原字符串的尾部
    str.endsWith('h');

    // repeat():       返回一个新字符串,表示将原字符串重复n次。
    'Q '.repeat(5); // Q Q Q Q Q

    // padStart():     用于头部补全  二个参数,第一个参数指定字符串的最小长度,第二个参数补全的字符串
    let h = 'hello';
    h.padStart(4,'***')
    
    // padEnd():       用于尾部补全。
    '12345678'.padEnd(11,'***')

    //具体的使用:年月日  YYYY-MM-DD  2023-11-11
    '11'.padStart(10,'2023-MM-DD');
    '11-11'.padStart(10,'2023-MM-DD')
}

2. 数值

window.onload = function () {
    //数值扩展
    //ES5
    // parseInt()       函数可解析一个字符串,并返回一个整数。
    // parseFloat()     函数可解析一个字符串,并返回一个浮点数
    // ES6
    Number.parseInt('100.01'); //100
    Number.parseFloat('100.01#'); //100.01

    // Number.isInteger()用来判断一个数值是否为整数。返回布尔
    Number.isInteger(100.23) //false
    Number.isInteger(100) //true
    Number.isInteger(100.00) //true
    Number.isInteger('100') //false

    //ES5
    // Math.ceil()  上舍入     返回大于或等于一个给定数字的最小整数
    Math.ceil(3.1)  //4
    // Math.floor()   下舍入  返回小于或等于一个给定数字的最大整数
    Math.floor(3.9)  //3
    // Math.round()     返回一个数字四舍五入后最接近的整数
    Math.round(3.9) 

    //ES6
    // Math.trunc()     用于去除一个数的小数部分,返回整数部分。
    Math.trunc(3.1)  //3
    Math.trunc(3.9)  //3
    Math.trunc('3.9')  //3
    Math.trunc('-3.9')  //-3

    // Math.sign()       方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数
    // 值。
    //参数为正数,返回1
    //参数为负数,返回-1
    //参数0,返回0
    //其它值,返回NaN
    Math.sign(1) //
    Math.sign(-1) //
    Math.sign(0) //

    //运算符 **(指数运算符)
    console.log(2**2)  //4  相当于2的2次方
    a**=3; //a*a*a
    a+=1; // a= a+1;
    let a = 3;
    a**=2; //9
}

3. 数组

window.onload = function () {
    //数组的扩展
    // Array.of()
    // 定义:用于将一组值,转换为数组
    Array.of(1); //[1]
    Array.of(1,2,3); //[1,2,3]
    Array.of('a','b','c'); //['a', 'b', 'c']
    Array.of(['a','b','c']); //[['a', 'b', 'c']]
    Array.of(1,2,3).length  //3
    Array.of({id:1},{name:'jack'}) //[{id:1},{name:'jack'}]

    //定义数组
    var arr1 = new Array(3); //创建具有指定长度新数组
    console.log(arr1)  //[undefined,undefined,undefined]

    var arr2 = new Array('3'); //
    console.log(arr2)  //['3']

    var arr3 = new Array('hello'); //
    console.log(arr3)  //['hello']

    //
    var arr4 = new Array([3]); //
    console.log(arr4) //[[1]]

    var arr5 = new Array(['hello']); //
    console.log(arr5)  //[['hello']]

    //使用
    //计算数字2,4,6,8的和
    var items = Array.of(2,4,6,8);
    items.reduce((total,cur)=>total+cur,0);
    //reduce()累加  [1,2,3,4,5]
    //reduce(function(total,cur){return total+cur},10)

    // copyWithin()
    // 定义:将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组(会修改当前数组)
    // 接受三个参数copyWithin(target, start , end)
    // target(必需):从该位置开始替换数据。如果为负值,表示倒数。
    // start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算。
    // end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算。
    var lists = [1,2,3,4,5];
    //lists.copyWithin(0,3)//[4,5,3,4,5,]
    //0代表从0开始替换,3代表从3号位置到结束,覆盖原来1 2
    //lists.copyWithin(1,3) //[1,4,5,4,5]
    lists.copyWithin(0,3,4) //[4,2,3,4,5]
    lists.copyWithin(0,3,3) //[1,2,3,4,5]
    [1,2,3,4,5,6,7].copyWithin(1,2) //[1,3,4,5,6,7,7]
    [1,2,3,4,5,6,7].copyWithin(1,2,5) //[1,3,4,5,5,6,7]


    // fill()
    // 定义:用于将一个固定值替换数组的元素。
    // value(必需):填充的值。
    // start(可选):开始填充位置。
    // end(可选):停止填充位置 (默认为 array.length)。
    var color = ["blue", "Orange", "red", "green"];
    var str = 'hello';
    color.fill(str,1,4)//["blue", "hello", "hello", "hello"];
    color.fill(str,3,4) //["blue", "Orange", "red", "hello"];

    // find()
    // find()用于找出第一个符合条件的数组成员。它的参数是一个回调函数,,所有数组成员依次执行该回调
    // 函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回
    // undefined。
    // find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组
    var i = [1,5,10,15].find(function(item,index,arr){
        return item>8
    });
    console.log(i) //10

    var number = [1,3,5,7,8];
    var n = number.find(v=>v>6);
    console.log(n) //7

    var n2 = number.findIndex(v=>v>6);
    console.log(n2)  //3

    var color = ["blue", "Orange", "red", "green"];
    var str = 'hello';
    var i = color.findIndex(v=>v=='red');
    color.fill(str,i,3);

    // includes()
    // 定义:方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的 includes 方法类似

    //flat()
    //定义:用于将嵌套的数组“拉平”,变成一维的数组。
    const items1 = [1,2,[3,4],[5,6]];
    console.log(items1.flat()) //[1, 2, 3, 4, 5, 6]

    const items2 = [1,2,[3,4,[5,6]],[7,8]];
    console.log(items2.flat(2))  //2拉平级数

    const items3 = [1,2,[3,4,[[5,6,[7,8,[9,10]]],11]],[12,13]];
    console.log(items3.flat(Infinity))  //[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]

    //flatMap()
    const lists1 = [1,2,3];
    const lists2 = lists1.flatMap(v=>[v,v*2]); //[[1,2],[2,4],[3,6]]
    console.log(lists2)  //[1, 2, 2, 4, 3, 6]
}

4. 对象

window.onload = function () {
    //对象扩展
    const o1 = {};
    o1.name = 'jack';
    o1['age'] = 18;
    let v = 'value';
    o1[v] = '100';

    const o2 = {
        [v]:'abc',
        [`my${v}`]:'xyz'
    };
    console.log(o2);

    var str = "id" ||"name"
    const o5 = {
        [str]:1
    };

    //运算符 ?.  链判断运算符
    const o3 = {
        id:1,
        name:'any',
        getName:function(){
            return this.name
        }
    };
    o3?.id //=>o3 && o3.id
    o3?.name
    o3?.getName?.() || 1

    if(o3?.getName?.()=='abc'){}

    //?? 空值合并操作符
    let x = 10 ?? 1;
    console.log(x); //10

    let x1 = null ?? 1;
    console.log(x1); //1

    let x2 = undefined ?? 1;
    console.log(x2); //1
}

5. 函数

window.onload = function () {
    //函数扩展
    //rest参数  ...rest 数组
    //arguments 参数集合 类似数组
    function f(...rest) {
        //console.log(rest) //[ 10, 20, 30 ]
        let filterNum = rest.filter(v => v > 20);
        console.log(filterNum)  //[ 30 ]
    };
    f(10, 20, 30)

    //其它用法
    function f1(a, b, ...c) {
        console.log(c) //[30,40,50]
    };
    f1(10, 20, 30, 40, 50)

    //rest参数之后不能再有其它参数
    // function f1(a,...b,c){
    //     console.log(b) //error
    // };
    // f1(10,20,30,40,50)

    //题目:1 2 3 4 5  + 6
    function f2(a, ...b) {
        let sum = b.reduce((total, cur) => total + cur, a);
        return sum;
    }
    f2(6, 1, 2, 3, 4, 5)

    //箭头函数
    var f = v => v;
    //f变量名 = v参数 =>v函数体,返回值
    // var f = function(){
    //     return 'f'
    // };
    // var f = ()=>'f'

    // var f = function(n1,n2){
    //     return n1+n2
    // };
    // var f = (n1,n2)=>n1+n2;

    // var f = function(n1,n2){
    //     return {name:n1,id:n2}
    // };
    // var f = (n1,n2)=> ({name:n1,id:n2}); //返回对象,需要添加()
    // f('jack',1)

    // var f = function(n1,n2){
    //     if(n1>n2){
    //         //
    //     }
    // };
    // var f = (n1,n2)=>{
    //     if(n1>n2){

    //     }
    // }

    //定义默认值
    // var f = (x, y = 1) => x + y;
    // f(10);

    // let f = ({ x = 0, y = 0 } = {}) => [x, y];
    // f({ x: 3, y: 3 })

    // let f = function({x=0,y=0}={}){  //{x=0,y=0} = { x: 3, y: 3 } || {};
    //     return [x,y]
    // };
    // f({ x: 3, y: 3 })

    // [10,20,30,40,50].filter(v => v > 20);
    // [10,20,30,40,50].filter(function(v){
    //     return v>20
    // })
  
    //题目:1 2 3 4 5  + 6
    function f2(a, ...b) {
        let sum = b.reduce((total, cur) => total + cur, a);
        return sum;
    }
    f2(6, 1, 2, 3, 4, 5)

    let f3 = (a,...b)=>b.reduce((total, cur) => total + cur, a);
    f3(6, 1, 2, 3, 4, 5)

    //箭头函数 简化了函数的定义
    //箭头函数的坑
    //1、箭头函数不能当作构造函数, 不可以使用new 命令
    let Fn=(name)=>{
        this.name = name;
    };
    var fn = new Fn('jack'); //Fn is not a constructor

    //2、箭头函数没有原型对象
    function fun(){}
    fun.prototype;

    var fun2 =()=>{}
    fun2.prototype;

    //3、不可以使用arguments对象   ...rest
    let fn3 = ()=>{
        console.log(arguments[2]) //arguments is not defined
    };
    fn3(1,2,3,4,5,6)

    //4、this指向
    // let x = 'x';
    // let obj = {
    //     x:'x2',
    //     getX:function(){
    //         console.log(this) //this谁调用指向的是谁,obj
    //     }
    // };
    // obj.getX(); //this.x = obj.x = 'x2'

    let x = 'x';
    let obj = {
        x:'x2',
        getX:()=>{  //getX是obj中的方法,key:value的定义方式
            console.log(this) //this===>window
        }
    };
    obj.getX();
    //箭头函数中this指向规则:箭头函数不绑定this,它会捕获其所在(定义的位置)上下文的this的值,作为自己的this值

    //call() apply() bind()
    // var id = 1;
    // function fn(){
    //     return this.id
    // };
    // var o1 = {
    //     id:100
    // };
    // //fn()函数中this的指向o1(相当于fn在o1对象中执行)
    // fn.apply(o1);


    var id = 1;
    let fn = ()=> this.id;
    var o1 = {
        id:100
    };
    //fn()函数中this的指向o1(相当于fn在o1对象中执行)
    fn.apply(o1); //1
}

6. 运算符

window.onload = function () {
	//扩展运算符 ...
    //将数组或对象展开
    let arr1 = [10,20,30]; //...arr1  ===> 10,20,30

    //1、需求 合并二个数组
    let arr2 = [1,2,3];
    let arr3 = [4,5];
    arr2.concat(arr3); //es5
    //ES6
    [arr2,arr3]; //[[1,2,3],[4,5]]
    [...arr2,...arr3] 
    
    //练习1
    let sum=(x,y)=>x+y;
    const items = [10,20];
    sum(...items); //sum(10,20);

    //练习2
    function fn(...value){ //...value (rest参数)
        console.log(value); //[-1,0,1,2,3]
    };
    const args = [0,1];
    fn(-1,...args,2,...[3]) //扩展运算符

    //练习3
    var a = [];
    function f(x,...y){  //x = a=[],...y = [[1,2,3,4]]
        x.push(...y); //...y = [1,2,3,4]
    };
    f(a,[1,2,3,4]);
    console.log(a); //[[1,2,3,4]]


    //在对象中运用
    let o1 = {id:1};
    let o2 = {name:'o2'};
    //合并 {id:1,name:'o2}
    let o3 = {...o1,...o2}; //{...o1,o2}  ====>{id:1,o2:{name:'o2}}

    let fn = ()=>123;
    let o4 = {...o3,fn};
    console.log(o4)  //{ id: 1, name: 'o2', fn: [Function: fn] }

    let o5 = { id: 1, name: 'o2'};
    let o6 = {...o5,count:100};
    console.log(o6) //{ id: 1, name: 'o2', count: 100 }


    //扩展题
    let array = [
        {id:1,name:'a'},
        {id:3,name:'a'},
        {id:10,name:'a'},
        {id:4,name:'a'},
        {id:27,name:'a'},
        {id:50,name:'a'},
    ];
    //求id的最大值
    let maxId = Math.max(...array.map(v=>v.id));
    console.log(maxId) //50
    //分解步骤
    //1、求最大值 Math.max(10,20,30)
    //2、数组中的id转换为1,3,10,4
    array.map(function(v){
        return v.id
    }) //[1,3,10,4]


    //深拷贝,浅拷贝
    var arr = [1,2,[3,4]];
    var arr3 = [...arr];
    arr[2][0] = 100;
    console.log(arr3); //[ 1, 2, [ 100, 4 ] ]
}

四、新的数据结构

1. set

set类似于数组,但是成员的值都是唯一的,没有重复的值。

set本身是一个构造函数,用来生成 Set 数据结构。

window.onload = function () {
    //set数据结构  表示集合 成员值是唯一的
    // const arr = new Array();
    // arr[0] = 0;
    // arr.push(1);
    // const obj = new Object();
    // obj.id=1;
    // obj['name'] = 'name';
    const s = new Set();
    //方法和属性
    //1、添加 add()
    s.add(1);
    //console.log(s);  //Set(1) { 1 }
    s.add(2);
    s.add(3);
    s.add(4).add(5).add(4).add(3).add(2).add('2')
    //console.log(s)  //Set(6) { 1, 2, 3, 4, 5, '2' } 成员值是唯一的

    //2、delete(value) 删除某个值,返回一个布尔值,表示删除是否成功。
    s.delete(4);
    //console.log(s)  //Set(5) { 1, 2, 3, 5, '2' }

    //3、clear() 清除所有成员,没有返回值。
    //s.clear();
    //console.log(s)  //Set(0) {}

    //4、has(value); 返回一个布尔值,表示该值是否为 Set 的成员。
    //console.log(s.has(3)) //true
    //if(s.has(3)){}

    //5、size属性:返回 set结构的成员总数
    console.log(s.size)  //5

    //简化定义
    const s1 = new Set([1,2,3,4,5,6,2,3,5,2,1,2,3,5,6,4]);
    console.log(s1); //Set(6) { 1, 2, 3, 4, 5, 6 }

    //扩展题
    //数组去重
    const array = [1,2,3,5,3,5,6,4,4,2,5,2,3,5,2,3,3,5,6,4,2,4,5,2,4,2];
    //ES5
    // function fn(){
    //     var arr = [];
    //     for(var i=0;i<array.length;i++){
    //         if(arr.indexOf(array[i])==-1){
    //             arr.push(array[i])
    //         }
    //     };
    //     return arr;
    // };
    // fn();  // [1, 2, 3, 5, 6, 4]

    //ES6
    const s3 = [...new Set(array)];  
    console.log(s3);  //[1, 2, 3, 5, 6, 4]

    //类型转换
    const s4 = new Set([1,2,3,2,1]);
    //set数据结构转换数组
    //1) 扩展运算符
    [...s4];
    //2) Array.from()
    const arr1 = Array.from(s4);

    //去重
    const arr3 = [1,2,3,2,1];
    //1)
    [...new Set(arr3)]
    //2)
    Array.from(new Set(arr3));

    //面试题
    //1、求出大于40的数据且去重处理
    const items = [10,20,35,25,35,23,45,24,26,46,24,26,354,64,24,10,20,35,23,46,354,354,23,46];
    console.log([...new Set(items.filter(v=>v>40))]) //[ 45, 46, 354, 64 ]
    //解析过程
    //1、求出大于40
    var c1 = items.filter(v=>v>40); //返回的是数组
    //2、去重
    var c2 = new Set(c1);
    //3、类型转换
    var c3 = [...c2];
    //4、代码合并
    var c4 = [...new Set(items.filter(v=>v>40))];

    //并集,交集,差集
    const lists1 = [1,2,3,4,5,6,4,5,6];
    const lists2 = [4,5,6,7,8,9,7,8,9];
    //并集
    //[...new Set([...lists1,...lists2])];

    //交集
    // [...new Set(lists1.filter(v=>lists2.includes(v)))]; 
    // [...new Set(lists2.filter(v=>lists1.includes(v)))]; 

    //差集
    //console.log([...new Set(lists1.filter(v=>!lists2.includes(v)))])  //[ 1, 2, 3 ]
    console.log([...new Set(lists2.filter(v=>!new Set(lists1).has(v)))]) //[ 7, 8, 9 ]
}  

2. map

Object本质上是键值对的集合,但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。

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

window.onload = function () {
    //map数据结构  
    const m1 = new Map();
    //1、添加数据 set(key,value)
    m1.set('id',1).set('name','jack');
    //console.log(m1) //Map(2) { 'id' => 1, 'name' => 'jack' }
    //其它类型
    let arr = [1,2,3];
    m1.set(arr,100);
   // console.log(m1) //Map(3) { 'id' => 1, 'name' => 'jack', [ 1, 2, 3 ] => 100 }
    //另一处定义方式
   //const m2 = new Map([['a',1],['b',2],['c',3]]);

   //2、size属性
   //console.log(m1.size) //3

   //3、get(key) 读取 key 对应的键值,如果找不到 key ,返回 undefined 。
   // console.log(m1.get(arr)) //100

    //4、has(key) 返回一个布尔值,表示某个键是否在当前 Map 对象之中。
    //console.log(m1.has('id')) //true

    //5、delete(key) 删除某个键,返回 true 。如果删除失败,返回 false 。
    m1.delete('id')
    console.log(m1.has('id')) //false

    //6、clear() 清除所有成员,没有返回值。
    m1.clear()


    //数据结构转换
    //1、对象转数组
    const obj1 = {id:1,value:'v'};
    Object.keys(obj1); //['id','value'];
    Object.values(obj1); //[1,'v']

    //2、map转数组
    const m2 = new Map([['a',1],['b',2],['c',3]]);
    // console.log(m2.keys())  //{ 'a', 'b', 'c' }
    [...m2.keys()];
    [...m2.values()];
    [...m2];

    const s4 = new Set([1,2,3,2,1]);
    //3、set数据结构转换数组
    //1) 扩展运算符
    [...s4];
    //2) Array.from()
    const arr1 = Array.from(s4);

    //4、对象转map
    const obj2 = {id:1,value:'v'};
    const m3 = new Map();
    for(let k in obj2){
        m3.set(k,obj2[k])
    };
    console.log(m3)  //Map(2) { 'id' => 1, 'value' => 'v' }

    //2)
    let m4 = new Map(Object.entries(obj2));

    //5、map转对象
    const m5 = new Map([['a',1],['b',2],['c',3]]);
    const o5 = {};
    for(let [k,v] of m5){
        o5[k] = v;
    };
    console.log(o5) //{ a: 1, b: 2, c: 3 }


    //面试题
    const items = [{id:1,name:'a'},{id:2,name:'b'},{id:1,name:'a'},{id:3,name:'c'},{id:2,name:'b'}
    ,{id:4,name:'d'},{id:1,name:'a'},{id:3,name:'c'},{id:4,name:'d'},{id:2,name:'b'}];
    //去重
    //方式一:
    // const items2 = []; //新的数组
    // var o = {}; //临时对象
    // for(var i=0;i<items.length;i++){
    //     if(!o[items[i].id]){
    //         o[items[i].id] = true; //
    //         console.log(o)
    //         items2.push(items[i]); //新的数据追加到数组
    //     }
    // };
    // console.log(items2)

    //方式二:has()
    // const map = new Map();
    // for(let v of items){
    //     if(!map.has(v.id)){
    //         map.set(v.id,v.name)
    //     }
    // };
    //console.log(map); //Map(4) { 1 => 'a', 2 => 'b', 3 => 'c', 4 => 'd' }
    //map转为数组对象
    //console.log([...map]) //[ [ 1, 'a' ], [ 2, 'b' ], [ 3, 'c' ], [ 4, 'd' ] ]
    //console.log([...map].map(([k,v])=>({id:k,name:v})))
    //另一种方式
    // const a = Array.from(map,([k,v])=>({id:k,name:v}));
    // console.log(a)

    //方式三:
    // const map = new Map();
    // for(let v of items){
    //     if(!map.has(v.id)){
    //         map.set(v.id,v)
    //     }
    // };
    // console.log([...map.values()])

    //方式四:
    // const map = new Map();
    // const c = items.filter(v=>{
    //     if(!map.has(v.id)){
    //         return map.set(v.id,v.name)
    //     }
    // });
    // console.log(c);
    //简化
    const map = new Map();
    console.log(items.filter(v=>!map.has(v.id) && map.set(v.id,v.name)))
}  

3. for...of...

ES6 借鉴 C++、Java、C# 和 Python 语言,引入了 for...of 循环,作为遍历所有数据结构的统一的方 法。

for...of 循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如 象、DOM NodeList 对象)、后文的 Generator 对象,以及字符串。

window.onload = function () {
    //for...of...
    const arr = ['red', 'green', 'blue'];
    for(let v of arr) {
        console.log(v); //'red', 'green', 'blue'
    };
    // for(let [i,v] of arr.entries()) { //
    //     console.log(i); //0 1 2
    // };

    const s = new Set(arr);
    for(let v of s) {
        console.log(v); //'red', 'green', 'blue'
    };


    const m = new Map();
    m.set('id',1).set('name','jack');
    for(let [k,v] of m) {  //[['a',1],['b',2],['c',3]]
        console.log(k); 
        console.log(v); 
    };

    const o1 = {
        id:1,
        value:'hello'
    };
    // for(let v of o1) {
    //     console.log(v);   //o1 is not iterable
    // };
    //解决方法
    // for(let v of Object.keys(o1)) {
    //     console.log(v);   
    // };
    for(let [k,v] of Object.entries(o1)) {
        console.log(k);   
        console.log(v);  
    };

    //
    var arr2 = [1,2,3,4,5];
    // arr2.forEach((v,i)=>{
    //     if(v==3){
    //         break; //Illegal break statement  
    //     };
    //     console.log(v)
    // })
    for(let v of arr2){
        if(v==3){
            continue; 
        };
        console.log(v)
    };
}  

4. symbol

新的原始数据类型,表示独一无二的值

解决问题:如果添加的属性名出现重复,如何保证属性名的唯一性,解决属性名的冲突

凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突

window.onload = function () {
    //Symbol数据类型
    const obj = {id:1,id:2,id:3};
    console.log(obj); //{ id: 3 }

    //声明方式
    let s = Symbol('a');  //let str = 'hello';
    //symbol的描述

    //实际的应用
    const o1 = {};
    o1.id = 10;
    o1.id = 20;
    //添加相同的属性
    var id = Symbol('id1');
    o1[id] = 30;
    var id = Symbol('id2');
    o1[id] = 40;
    console.log(o1)  //{ id: 20, [Symbol(id1)]: 30, [Symbol(id2)]: 40 }

    var id = Symbol('id3') //
    //第二种方式
    let o2 = {
    id:10,
    [id]:20,
    [Symbol('id3')]:30,
    [Symbol('id4')]:40,
    };
    //console.log(o2) //{ id: 10, [Symbol(id3)]: 20 }
    //获取id
    //console.log(o2.id) //10
    //获取symbol   o2[Object.getOwnPropertySymbols(o2)[2]]
    console.log(o2[Object.getOwnPropertySymbols(o2)[2]])

    //Symbol.for('abc') 登记机制
    var s1 = Symbol.for('abc');
    var s2 = Symbol.for('abc');
    console.log(s1===s2)  //true

    var s3 = Symbol('abc');
    var s4 = Symbol('abc');
    console.log(s3===s4)  //false

    let o3 = {};
    var a = Symbol('a1');
    o3[a] = 100;
    var a = Symbol('a1');
    o3[a] = 100;
    var a = Symbol('a1');
    o3[a] = 100;
    console.log(o3) //{ [Symbol(a1)]: 100, [Symbol(a1)]: 100, [Symbol(a1)]: 100 }


    let o4 = {};
    var a = Symbol.for('a1');
    o4[a] = 100;
    var a = Symbol.for('a1');
    o4[a] = 100;
    var a = Symbol.for('a1');
    o4[a] = 100;
    console.log(o4)   //{ [Symbol(a1)]: 100 }
}  

5. Promise

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的 结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API, 各种异步操作都可以用同样的方法进行处理。

window.onload = function () {
	//Promise
	//图片加载,获取到图片后的处理
	function LoadImg(){
		return new Promise((resolve,reject)=>{
				const img = new Image();
				img.onload = function(){
					resolve(img)
				};
				img.onerror = function(){
					reject('error')
				}
		})
	};
	LoadImg.then(res=>{
		console.log(res)
	})
	

	//解决回调陷阱
	let promise = new Promise((resolve,reject)=>{
		if(true){
				resolve('111')
		}else {
				reject('error')
		}
	});
	promise
	.then(res=>{
		//处理第一个异步操作的结果
		console.log(res);
		return new Promise((resolve,reject)=>{
				if(true){
					resolve('222')
				}else {
					reject('error')
				}})
	})
	.then(res=>{
		//处理第二个异步操作的结果
		console.log(res)
		return new Promise((resolve,reject)=>{
				if(false){
					resolve('222')
				}else {
					reject('error')
				}})
	})
	.then(res=>{
		//处理第三个异步操作的结果
		console.log(res)
	})
	.catch(err=>{
		//处理错误
		console.log(err)
	})


	//promise.all()和promise.race()
	//如何同时触发多个promise,用于处理并发任务,多个异步都结束在执行下一个任务
	let p1 = new Promise((resolve,reject)=>{
		console.log('第一个异步任务');
		resolve('第一次异步结果')
	});
	let p2 = new Promise((resolve,reject)=>{
		console.log('第二个异步任务');
		resolve('第二次异步结果')
	});
	let p3 = new Promise((resolve,reject)=>{
		console.log('第三个异步任务');
		resolve('第三次异步结果')
	});
	Promise.all([p1,p2,p3])
	.then(arr=>{
		console.log(arr)
	});

	//其中有失败的状态
	let p4 = new Promise((resolve,reject)=>{
		console.log('第一个异步任务');
		resolve('第一次异步结果')
	});
	let p5 = new Promise((resolve,reject)=>{
		console.log('第二个异步任务');
		reject('第二次异步失败')
	});
	let p6 = new Promise((resolve,reject)=>{
		console.log('第三个异步任务');
		resolve('第三次异步结果')
	});
	Promise.all([p4,p5,p6])
	.then(arr=>{
		console.log('三次成功');
		console.log(arr)
	})
	.catch(err=>{
		console.log('失败');
		console.log(err)
	})

	//promise.race(),返回最先执行完成的promise的结果
	let p7 = new Promise((resolve,reject)=>{
		console.log('第一个异步任务');
		setTimeout(()=>{
				resolve('第一次异步结果')
		},3000)
	});
	let p8 = new Promise((resolve,reject)=>{
		console.log('第二个异步任务');
		setTimeout(()=>{
				resolve('第二次异步结果')
		},1000)
	});
	let p9 = new Promise((resolve,reject)=>{
		console.log('第三个异步任务');
		setTimeout(()=>{
				resolve('第三次异步结果')
		},2000)
	});
	Promise.race([p7,p8,p9])
	.then(res=>{
		console.log('成功');
		console.log(res)
	});
}  

6. async-await

有了 async-await、promise 还有必要学习吗?

async-await是promise和generator的语法糖。只是为了让我们书写代码时更加流畅,当然也增强了代 码的可读性。

简单来说:async-await 是建立在 promise机制之上的,并不能取代其地位。

window.onload = function () {
    //async
    //async-await是promise和generator的语法糖。只是为了让我们书写代码时更加流畅,当然也增强了代码的可读性。
    async function f(){  //
        //console.log(1)
        return 'async'
    };
    //函数前添加关键字async  async返回一个promise实例,默认是成功的状态,
    //会把f()函数的返回值传给then方法的第一个参数
    f().then(res=>{
        console.log(res)
    });

    //await 操作符
    // await 后面是一个promise对象,但是它也可以跟其它值,比如字符串,数值,普通函数
    console.log(1);
    async function f(){
        console.log(2);
        await 100; //后面的代码进入到等待 微任务
        console.log(3);
    };
    f();
    console.log(4);
    //1 2 4 3

    //一
    function f2(){
        console.log(1)
    };
    console.log(2);
    async function f1(){
        console.log(3);
        await f2();
        console.log(4);
    };
    f1();
    console.log(5)

    //二
    function promise(){
        return new Promise(resolve=>{
            resolve(1)
        })
    }
    async function f(){  
        console.log(2)
        var res = await promise();
        console.log(res)
    };
    f();
    console.log(4)
}

五、微任务和宏任务

JS 执行机制

JS语言的一大特点:单线程

所有执行的任务都排队,任务分编号,只有1号办理完,才能办理2号

同步任务和异步任务

  • 同步任务:会在主线程上执行的任务
  • 异步任务:不进入主线程,进入任务队列,只有等主线程任务执行完毕,任务队列开始通知主线程,请求执行任务
实际场景:6个人排队办理业务
1、正常======下一步 同步任务
2、正常======下一步 同步任务
3、异常======突发事件忘带身份证 ====任务队列        异步任务-宏任务
4、正常======下一步 同步任务
5、异常======突发事件手机没电 ====任务队列        异步任务-微任务
6、异常======突发事件手机没电 ====任务队列        异步任务-微任务

微任务和宏任务

  • 紧急的小事,插队优先处理,promise的then,async await
  • 计划中的大事,按顺序处理,setTimeout setInterval
  • JavaScript总是先完成当前任务,然后处理所有微任务,最后才处理下一个宏任务
就像去银行办业务一样,先要取号进行排号。
一般上边都会印着类似:“您的号码为XX,前边还有XX人。”之类的字样。
由于柜员只能处理一个来办理业务的客户,这时每一个来办理业务的人就可以认为是银行柜员的一个宏任务来存在的;
当柜员处理完当前客户的问题以后,选择接待下一位,广播报号,也就是下一个宏任务的开始。
所以多个宏任务合在一起就可以认为有一个任务队列在这,里边是当前银行中所有排号的客户。
如果叫到你的时候你不在,那么你当前的号牌就作废了,柜员会选择直接跳过进行下一个客户的业务处理,等你回来以后还需要重新取号
而且一个宏任务在执行的过程中,是可以添加一些微任务的,就像在柜台办理业务,你前边的一位老大爷可能在存款,结果在存款这个业务办理完以后,柜员会问老大爷还有没有其他需要办理的业务,这时老大爷想了一下:“想选择稳一些的理财”,这时候柜员肯定不能告诉老大爷说:“您再上后边取个号去,重新排队”。
所以本来快轮到你来办理业务,会因为老大爷临时添加的“理财业务”而往后推。
也许老大爷在办完理财以后还想 再办一个信用卡?或者 再买点儿纪念币?
无论是什么需求,只要是柜员能够帮她办理的,都会在处理你的业务之前来做这些事情,这些都可以认为是微任务。
在当前的微任务没有执行完成时,是不会执行下一个宏任务的。

promise是同步还是异步

  • 可以确定Promise本身是同步的
  • 他的then方法和catch方法是异步的
//Promise是同步
console.log(1);  //同步任务
let p = new Promise(resolve=>{
    console.log(2);   //同步任务
    resolve(3)
});
p.then(res=>{  //异步任务  微任务
    console.log(res)
});
console.log(4);  //同步任务

//相关面试题
setTimeout(()=>{  //宏任务
    console.log(0)
},100)
console.log(1);  //同步任务
let p1 = new Promise(resolve=>{
    console.log(2);   //同步任务
    resolve(3)
});
p1.then(res=>{  //异步任务  微任务
    console.log(res)
});
console.log(4);

六、Proxy

proxy 在目标对象的外层搭建了一层拦截,外界对目标对象的某些操作,必须通过这层拦截

var proxy = new Proxy(target, handler);

Proxy的作用

  • 拦截和监视外部对对象的访问
  • 降低函数或类的复杂度
  • 在复杂操作前对操作进行校验或对所需资源进行管理

Proxy所能代理的范围--handler

实际上 handler 本身就是 ES6 所新设计的一个对象.它的作用就是用来 自定义代理对象的各种可代理操 作 。

它本身一共有13中方法,每种方法都可以代理一种操作.其13种方法如下:

get(target, propKey, receiver):
拦截对象属性的读取,比如proxy.foo和proxy['foo']。
set(target, propKey, value, receiver):
拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
has(target, propKey):
拦截propKey in proxy的操作,返回一个布尔值。
deleteProperty(target, propKey):
拦截delete proxy[propKey]的操作,返回一个布尔值。
ownKeys(target):
拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、
Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而
Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
getOwnPropertyDescriptor(target, propKey):
拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
defineProperty(target, propKey, propDesc):
拦截Object.defineProperty(proxy, propKey, propDesc)、
Object.defineProperties(proxy, propDescs),返回一个布尔值。
preventExtensions(target):
拦截Object.preventExtensions(proxy),返回一个布尔值。
getPrototypeOf(target):
拦截Object.getPrototypeOf(proxy),返回一个对象。
isExtensible(target):
拦截Object.isExtensible(proxy),返回一个布尔值。
setPrototypeOf(target, proto):
拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种
额外操作可以拦截。
apply(target, object, args):
拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、
proxy.apply(...)。
construct(target, args):
拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。

Proxy场景

# 1. 实现私有变量,想获取angelababy,由于添加了拦截获取的只能是18
var target = {
    name: 'angelababy',
    _age: 30
}
var handler = {
    get: function(target,key){
        if(key.startsWith('_')){  
            console.log('私有变量age不能被访问')
            return 18
        }
        return target[key];
    },
    set: function(target, key, value) {
        if(key.startsWith('_')){
            console.log('私有变量age不能被修改')
            return 18
        }
        target[key] = value;
    }
} 
var a = new Proxy(target, handler);
a._age  //18 私有变量age不能被访问
a._age = 20  //私有变量age不能被修改


# 2. 抽离校验模块
var num = {  
    count: 0,
    id: 1234,
    total: 14
};
var handler = {  
    set(target, key, value, proxy) {
        if (typeof value !== 'number') {
            throw Error("只能输入数字");
        }
        return Reflect.set(target, key, value, proxy); 
    }
};
var p = new Proxy(num, handler);
// 抛出错误,因为 "123" 不是数值型
p.count = "123";
// 赋值成功
p.count = 333;

Object.defineProperty()与Proxy的异同

  • Object.defineProperty()
    • 优点:可以更好的拦截
    • 缺点:无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应(虽说对常用 的方法进行了处理,但然存在局限性);只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。
  • Proxy
    • 优点:可以劫持整个对象,并返回一个新对象;有13种劫持操作
    • 缺点:兼容性不好
var Obj = {}
Object.defineProperty(Obj, 'a', {
     get: function () {
           console.log('get');
           return v
     },
     set: function (val) {
           console.log('set');
           v = val
     }
});
Obj.a = [] // set 
Obj.a.push('1') // get 
Obj.a[0] = 1 // get
Obj.a.pop(1) // get 
Obj.a = [1, 2, 3] // 



var arr = [];
var p = new Proxy(arr, {
     get: (target, key) => {
	      console.log('get')
          return key in target ? target[key] : undefined
     },
     set: (target, key, value) => {
          console.log('set')
          target[key] = value
          return true
     }
})
p.push(1);
get  // 获取数组arr的push方法
get  // 获取数组arr的length属性
set  // 设置arr[0] = 1
set  // 设置数组arr长度为1

七、class

  • ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键 字,可以定义类
  • ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到
window.onload = function () {
    //class类
    //定义类
    class Animal {
        constructor(name){
            this.name = name;
        }
        speak(){
            console.log(this.name + `like to bark`)
        }
    }
    let animal = new Animal('Dog');
    animal.name
    animal.speak();

    //ES5定义构造函数
    function Animal2(name){
        this.name = name;
    }
    Animal2.prototype.speak = function(){
        console.log(this.name + `like to bark`)
    };
    let animal2 = new Animal2('Cat');
    animal2.name;
    animal2.speak()

    //扩展
    //获取XY坐标,传入X,Y;返回[x,y]
    //ES5
    // function Point(x,y){
    //     this.x = x;
    //     this.y = y;
    // };
    // //生成坐标
    // Point.prototype.coordinate = function(){
    //     return [this.x,this.y]
    // };
    // let p1 = new Point(10,20);
    // p1.coordinate();

    //ES6 class
    class Point{
        constructor(x,y){
            this.x = x;
            this.y = y;
        }
        coordinate(){
            return [this.x,this.y]
        }
    };
    let p2 = new Point(100,200);
    p2.coordinate()

    //ES6 类,可以看作构造函数的另一种写法
    class Point {

    }
    //constructor方法是类的默认方法
    //类型
    //typeof Point //'function'
    //等同于
    class Point {
        constructor(){

        }
    };

    //类的实例 生成类的实例对象 通过new命令
    class Point {

    }
    let p3 = Point() //error  Class constructor Point cannot be invoked without 'new'

    //class继承
    //ES5构造函数继承
    function Animal(){
        this.type = "animal"
    };

    function Cat(name,color){
        this.name = name;
        this.color = color;
    }
    Cat.prototype = new Animal();
    let c1 = new Cat('Niki','black');
    c1.type

    //第二种方式
    function Animal(){
        this.type = "animal"
    };

    function Cat(name,color){
        Animal.apply(this);
        this.name = name;
        this.color = color;
    };
    let c2 = new Cat('Lily','white');
    c2.type

    //class的继承
    class Point{   //点
        constructor(){
            this.x = 10;
            this.y = 20;
        }
        coordinate(){
            return [this.x,this.y]
        }
    };

    class CircleDot extends Point{ //extends关键字 实现继承
        constructor(z){
            super();  //关键字 代表父类的构造函数
            //Point.prototype.constructor.apply(this)
            //Point.prototype.constructor = Point
            this.z = z;
        }
    };
    let circle = new CircleDot(30);
    circle.z

    //父类constructor有参数,处理的方式
    class Point{   //点
        constructor(x,y){
            this.x = x;
            this.y = y;
        }
        coordinate(){
            return [this.x,this.y]
        }
    };
    class CircleDot extends Point{ //extends关键字 实现继承
        constructor(x,y,z){
            super(x,y);  //关键字 代表父类的构造函数
            this.z = z;
        }
    };
    let circle2 = new CircleDot(10,20,30);
    circle2

    //当子和父属性重名,怎么办?
    class Point{   //点
        constructor(x,y){
            this.x = x;
            this.y = y;
        }
        coordinate(){
            return [this.x,this.y]
        }
    };
    class CircleDot extends Point{ //extends关键字 实现继承
        constructor(x,y,z){
            super(x,y);  //关键字 代表父类的构造函数
            this.x = z;
        }
        coordinate(){
            console.log(super.coordinate());  //super.coordinate() 父的方法
            console.log('CircleDot')
        }
    };
    let circle3 = new CircleDot(10,20,30);
    circle3.coordinate()

    //多重继承   一个类同时继承多个父类
    //class不直接支持多重继承,只支持一个类继承,不支持从多个类同时继承
    class  Animal{
        constructor(){
            console.log('Animal')
        }
    }
    class  Cat extends Animal{
        constructor(){
            super();
            console.log('Cat')
        }
    }

    class Dog{
        constructor(){
            console.log('Dog')
        }
    }

    class Test  extends Cat,Dog{  //error Classes can only extend a single class.
        constructor(){
            super();
            console.log('Test')
        }
    }

    let t1 = new Test();

    //间接方式:
    //想继承特性
    //定义一个混合特性1-吃  (很多对象都想拥有的特性) mixin
    let EatMixin = {
        eat(){
            console.log('eat')
        }
    }

    //定义一个混合特性2-睡
    let SleepMixin = {
        sleep(){
            console.log('sleep')
        }
    }

    class  Animal{
        constructor(){
            console.log('Animal')
        }
    }
    Object.assign(Animal.prototype,EatMixin,SleepMixin); //混合特性 

    class  Cat extends Animal{
        constructor(){
            super();
            console.log('Cat')
        }
    }
    class Test  extends Cat{  //error Classes can only extend a single class.
        constructor(){
            super();
            console.log('Test')
        }
    }
    let test2 = new Test();
    test2.eat()
    test2.sleep();
}