es6基础知识

es5补充:

开启严格模式的三个作用:

  • 变量要先定义后使用

  • 改变this指向(普通函数的this不再指向全局)

  • 使用eval()函数产生作用域;

​ eval()功能:运行js字符串格式的表达式

<script>
        'use strict'; //开启严格模式

        //开启严格模式有三个功能:
        //1、变量要先定义后使用

        // age = 20;   // age is not defined
        var age;
        age = 20;
        console.log(age, 999);


        //2、改变this指向

        function Demo(x, y) {
            console.log(this, 888);
            this.age = x;
            this.usr = y;
            console.log(this.age, this.usr);//this指向函数
        }

        // demo(21, 'tom');
        // new Demo(22, 'tom');

        //3、使用eval()函数产生作用域

        //eval()功能:运行js字符串格式的表达式
        var str = '1+8';
        // alert(str);
        // alert(eval(str));

        var usr = 'lisi';
        var str2 = 'var usr="zhangsan";alert(usr);';
        eval(str2);
        alert(usr);
    </script>

JSON对象:

  • 什么是json及json用途?

json(JavaScript Object Notation:javascript对象节点),它是一种轻量级的数据交换格式。

josn用途?

josn主要用来在不同编程语言之间交换数据。

  • JSON.parse()、JSON.stringify()
  <script>
        //1、为什么要使用json?
        //josn主要用来在不同编程语言之间交换数据,当然也可以存储数据

        //2、JSON.parse()、JSON.stringify()

        var stu = '{"usr":"lisi","age":20}';
        var strobj = JSON.parse(stu);
        console.log(strobj, typeof strobj);

        var stu2 = JSON.stringify(strobj);
        console.log(stu2, typeof stu2);

        //3、json有两种数据格式:   
        //3.1、json对象:{"属性名1":"值1","属性名2":'值2'}

        var zs = {
            "xh": "zs006",
            'name': "zhangsan"
        };
        //console.log(zs.name);

        //3.2、json对象数组:[{},{},{}]

        var xuesheng = [{
            "xh": "zs006",
            "name": "zhangsan",
        }, {
            "xh": "ls009",
            "name": "lisi",
        }];

        console.log(xuesheng[1].name);
    </script>

Object对象:

  1. Object.create():以某个对象为原型创建新对象的同时可以扩展新的属性

​ (1)、 Object.create(对象, 新属性{value:xxx,[writable,configurable,enumerable]})

​ * 作用: 以指定对象为原型创建新的对象

​ * 为新的对象指定新的属性, 并对属性进行描述

​ value : 指定值

​ writable : 标识当前属性值是否是可修改的, 默认为false

​ configurable: 标识当前属性是否可以被删除 默认为false

​ enumerable: 标识当前属性是否能用for in 枚举 默认为false 遍历


 <script>
        //Object.create(对象,选项):以某个对象为原型创建新对象的同时可以扩展新属性

        var obj = {
            "usr": "lisi",
            "age": 20
        };

        // obj.phone = 119;

        var obj2 = Object.create(obj, {
            "email": {
                value: "lisi@163.com",
                writable: true, //设置属性值可以修改
                configurable: true, //设置属性可以删除
                enumerable: true //设置属性可以遍历
            },
            "tel": {
                value: 110
            }
        });

        obj2.email = 'tom@qq.com'; //改变属性值
        //删除属性
        // delete obj2.email;
        console.log(obj2, obj2.age, obj2.email);

        //循环对象:  
        for (var x in obj2) {
            // console.log(x, obj2[x]);
        }

    </script>

​ 2)Object.defineproperties():给指定对象扩展新的属性

​ Object.defineProperties(object, descriptors)

​ Object.defineProperties(对象,选项):给指定对象扩展属性

​ 作用: 为指定对象定义扩展多个属性

​ get :用来获取当前属性值得回调函数

​ set :修改当前属性值得触发的回调函数,并且实参即为修改后的值

<script>
        var obj = {
            name:'lisa',
            age:18
        }
        Object.defineProperties(obj,{
            abname:{//添加属性值abname
                get:function(){//获取时调用
                    // console.log("get....");
                    return this.name + this.age;
                },
                set:function(x){//设置时调用
                    var arr = x.split(' ')
                    this.name = arr[0];
                    this.age = arr[1];
                    // console.log("set...");
                }
            }
        });
        console.log(obj.abname);//获取
        obj.abname = 'li 12';//设置
        console.log(obj.abname);
        
       
    </script>

数组

array.filter():遍历数组的同时过滤元素

array.map():遍历数组的同时改变其中元素值

具体书写方式与用法如下:

<script>
        var fenshu = [80, 85, 70, 90, 69];
        //获取分数大于等于85分的分数
        var arr = fenshu.filter(function(el, ind) {
            // console.log(x, y);
            return el >= 85;
        });

        // console.log(arr);

        

        //给每个分数加5分
        var arr2 = fenshu.map(function(el, ind) {
            return el + 5;
        })

函数:

  • arguments

  • 通常有return

  • 改变this指向: call()、apply()、bind()

      	demo.bind(this新指向)(参数1, 参数2);
    
<script>
        function demo() {
            console.log(arguments, this);//1,21 windows
        }

        // demo(1, 21);

        document.onclick = function() {
            // demo(1, 3);
            // demo.call(this, 1, 3);
            // demo.apply(this, [1, 3]);
            //  demo.apply(null, [1, 3]);

            // var fn = demo.bind(this);
            // //  console.log(fn);
            // fn(11, 5);
            //等价于上面的写法
            demo.bind(this)(11, 5);

        }
    </script>

Es6

Es6 简介

2015年6月正式发布es6 ,以后每年6月份都会发布一个新版本,但变化不太;

ECMAScript与javascript之间的关系?

ECMAScript是规范、标准,而javascript是规范的实现。

Es6新特性–let与var

1、let作用:用来声明变量:

2、let与var的别:

1)、作用域不同

​ let定义的变量是块级作用域 ;

​ var定义的变量是函数作用域[局部的], 但在if、for等定义的变量是全局的。

2)、在同一个作用域中同一个变量let只能声明一次,而var可以多次声明,let声明的变量可以避免全局变量被污染。

3)、var声明的变量存在变量提升,而let则没有变量提升,let声明的变量要先定义后使用。

  1. let有暂时性死区:let声明的变量要先定义后使用。

5)、let声明的变量不会绑定到顶层对象(顶层对象可以理解为最大的全局变量,即window对象)上。

let的总结:暂时忘记var,尽量使用let

3、let应用:模拟选项卡功能

作业:

​ 使用let 改写前面的”选项卡”案例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .wrap{
            width: 300px;
            margin: 0 auto;
        }
        .con div{
            width: 200px;
            height: 200px;
            border: 1px solid #000;
            display: none;
        }
    </style>
</head>
<body>
    <div class="wrap">
        <button>军事</button>
        <button>体育</button>
        <button>娱乐</button>
        <button>购物</button>
        <div class="con">
            <div>内容1</div>
            <div>内容2</div>
            <div>内容3</div>
            <div>内容4</div>
        </div>
    </div>
    <script>
        let btnS = document.querySelectorAll('button');
        let conS = document.querySelectorAll('.con div');
        conS[0].style.display = 'block';
        for(let i = 0 ;i < btnS.length; i++){
            btnS[i].onclick = function(){
                for(let j = 0 ;j < conS.length; j++){
                    conS[j].style.display = 'none';
                }
                conS[i].style.display = 'block';
            }
        }
    </script>
</body>
</html>

es6的常量const

1、常量也是块级作用域、不存在声明提升(先定义后使用)、同一个常量只能声明一次;

2、常量一旦声明其值不能改变,但要注意:如果常量的值为数组、对象时其值是可以被改变的;(原因是数组,对象数据存放在堆区通过地址来取数据,地址并没有改变,改变的是堆区的数据。)

​ 什么是常量 ?

​ 在整个程序运行期间其值是固定不变量的,则可以考虑使用常量,比如:系统的域名、端口

​ 常量名习惯性大写,如:
​ const PI = 3.14;

   <script>

    const STU = {
        'usr': 'tom',
        'tel': 110
    };

    STU.tel = 119;
    console.log(STU);//110
</script>

总结:常量的使用场景:

  1. 如果在开发程序时遇到其值是相对固定的则可以声明为常量

  2. 开发项目时项目配置(如:数据库端口、主机名)则可以声明为常量

解构赋值

在es6中将数组、对象中的数据按照一定模式提取出来并赋值给变量,这一过程称为解构赋值。 解构赋值主要分为数组解构赋值、对象解构赋值

1,数组解构赋值:

​ 1)数组完全解构赋值

​ 2)数组不完全解构赋值 ,缺省解构。

​ 3)默认值

​ 4)数组嵌套解构赋值

​ 数组解构赋值语法: let [模式] = [数组]

注意:在数组解构赋值时要注意左边模式中的变量先后顺序,因为数组是一个有序的集合。

<script>
 
        //数组完全解构赋值:
        let fenshu = [60, 70, 90, 100];
        // let [x, y, m, n] = fenshu;
        //注意:数组解构赋值时要注意变量的先后顺序
        //let [m, n, x, y] = fenshu;
        //console.log(x, y, m, n);

        //数组不完全解构赋值:
        // let [, x, , y, m, z] = [1, 3, 7, 5, 9];
        //解构失败时默认为undefined
        // console.log(x, y, m, z);

        //默认值:在解构失败时默认值才生效
        //let [x, y, z = 99, n = 100] = [5, 3, 1];
        //console.log(x, y, z, n);

        //数组嵌套解构赋值:  
        let arr = [1, 3, [5, 6], 100];
        let [, x, [, y], z] = arr;
        console.log(x, y, z);
    </script>

2,对象解构赋值:

1)对象完全解构赋值

2)对象不完全解构赋值

3)默认值

4)对象嵌套解构赋值

对象解构赋值:let {模式} = {对象};

        //对象完全解构赋值   
        let obj = {
            "usr": "lisi",
            "age": 20,
            "email": "lisi@qq.com"
        };

        //完整写法:
        // let {
        //     "usr": x,
        //     "age": y,
        //     "email": m
        // } = obj;
        //console.log(x, y, m);
        // let {
        //     "usr": usr,
        //     "age": age,
        //     "email": email
        // } = obj;

        //简写形式:
        // let {
        //     usr,
        //     age,
        //     email
        // } = obj;
        //注意:对象解构赋值时变量名不区分先后顺序
        let {
            email,
            usr,
            age
        } = obj;
        // console.log(usr, age, email, 666);

        //对象不完全解构赋值:  

        // let {
        //     y,
        //     z,
        //     m
        // } = {
        //     "x": 11,
        //     "y": 33,
        //     "z": 77
        // };
        // console.log(y, z, m);


        //默认值:在解构失败时默认值才生效
        // let {
        //     price = 100,
        //         author = "中公教育"
        // } = {
        //     'bkname': "web开发",
        //     "price": 88
        // };
        // console.log(price, author);

        //对象嵌套解构赋值

        let obj2 = {
            "mytest": [1, 6, {
                "m": "ok",
                "n": 66
            }, 99]
        };

        let {
            "mytest": [, x, {
                n
            }, y]
        } = obj2;

        console.log(x, n, y);
    </script>

模板字符串:

1)模板字符串的语法:`` [反引号]

2)模板字符串的用途:用来简化字符串的拼接。

3)模板字符串的特点:

​ 在模板字符串中可以引用变量、函数调用、运算、换行。

4)模板字符串的应用:

<script>
        //模板字符串主要用来简化字符串拼接
        let usr = "lisi";
        let age = 20;
        // let str = '姓名:' + usr + " 年龄:" + age;
        let x = '菜单一'
            // let str2 = '<a href="">' + x + '</a>';

        let str2 = `<a href="">${x}</a>`;
        console.log(str2);
        //模板字符串特点:
        //在模板字符串中可以引用变量、调用函数、运算、换行

        let n = 99;
        let str = `姓名:${usr}  
        年龄: ${age}
        函数调用结果为:${demo()}
        运算结果为:${n+8}`;
        console.log(str);

        function demo() {
            return 'Hello WEB!';
        }
    </script>

数组扩展array.from(对象或者类数组)

array.from(对象或者类数组) 将伪数组对象或可遍历对象转换为真数组

<body>
    <div>11</div>
    <div>21</div>
    <div>31</div>
    <div>41</div>
    <script>
        let divs = document.getElementsByTagName('div');
        console.log(divs);
        //array.from():将伪数组或可遍历的对象转换成真数组

        //转换成数组
        let arrs = Array.from(divs);
        console.log(arrs);

        //可遍历的对象:
        let obj = {
            0: 'a',
            1: 'B',
            2: 'c',
            'length': 3 //注意这里
        };
        console.log(obj, typeof obj);
        let arrs2 = Array.from(obj);
        console.log(arrs2);
    </script>
</body>

对象简写形式应用:

<script>
        //对象简写形式:
        //就是在对象中可以使用变量和函数来作对象的属性名及方法

        let usr = "Tom";
        let email = "Tom@163.com";

        // let obj = {
        //     "usr": usr,
        //     "email": email
        // };
        //对象简写形式1:
        let obj = {
            usr,
            email
        };
        console.log(obj, 666);

        //对象简写形式2:
        let obj2 = {
            usr,
            email,
            // fn: function() {
            //     console.log('fn...');
            // }
            fn() { //方法简写形式
                console.log('fn...');
            }
        };
        obj2.fn();
        console.log(obj2);
    </script>

属性名表达式:

对象的属性名可以写表达式,而在es5中对象的属性名只能是字符串。

  <script>
     //属性名表达式:在es5中对象的属性名只能为字符串
        let y = 2;
        let m = 'age';
       	let obj = {
            "usr": "lisi",
			["usr" + y]: "Tom",
       };
       obj[m] = 20;
        console.log(obj[m]);

        console.log(obj, obj['usr' + y]);

    </script>

作业:

产生一个对象:{A:[],B:[],C:[],D:[]…Z:[]},提示:可以考虑使用属性名表达式

<script>
        // let i = 'Z'
        // console.log(i.charCodeAt(0));
        // let n = 65;
        // var nn = String.fromCharCode(n);根据ASCII值转字母的方法
        // console.log(nn);
        let obj = {};
        for(let i = 65; i <= 90; i++){
            let n = String.fromCharCode(i);
            obj[n] = [];
        }
        console.log(obj);
    </script>

扩展运算符:

1)扩展运算符语法: …

2)扩展运算符主要功能:

​ (1)类似于arguments,收集数据功能

​ (2)类似于apply,数据展开功能

<script>
        //扩展运算符语法: ...
        //扩展去处符的功能:
        //1、展开数据,类似于apply
        //2、收集数据,类似于arguments   

        //展开数据:  
        let arr = [1, 3, 4];
        let arr2 = [6, 8, ...arr];
        console.log(arr2);

        let obj = {
            'x': 11,
            "y": 66
        };

        let obj2 = {
            fn: function() {
                console.log('obj2...');
            }
        };

        let obj3 = {
            "data": {...obj
            },
            "method": {...obj2
            }
        };

        console.log(obj3);

        //收集数据:
        function demo(...y) { //收集数据
            console.log(y);
            // test(y[0], y[1], y[2]);
            test(...y); //展开数据
        }

        function test(m, n, z) {
            console.log(m, n, z);
        }

        demo('a', 1, 3);

        //收集数据
        function yesok(x, ...s) {
            console.log(x, s);
        }

        yesok(1, 2, 3);

	
    </script>

扩展运算符应用:

//扩展运算符应用
  		let arr1 = [1,2,3];
        let arr2 = [5,6]
        let arr3 = [...arr1,...arr2] ;
        console.log(arr3);

函数形参默认值

实例

<script>
        //计算器:
        jsq(11, 3);
        //函数形参默认值:
        //注意:设置了默认值的形参要放在所有形参的最右边
        function jsq(m, n, flg = '+') {//不传入运算符默认加法
            // function jsq(flg = '+', m, n) {  //错误的
            switch (flg) {
                case '+':
                    return m + n;
                    break;
                case '-':
                    return m - n;
                    break;
                case '*':
                    return m * n;
                    break;
                case '/':
                    return m / n;
                    break;
            }
        }


        jsq(1, 3);
    </script>

箭头函数

箭头函数主要用途:**箭头函数一般用于回调函数,而回调函数通常是匿名函数 **

箭头函数语法: ([形参…]) =>{}

箭头函数特点:

1)箭头函数不同于一般函数:

​ (1)一般函数可以先调用后定义,箭头函数只能先定义后调用;

​ (2)一般函数有arguments,箭头函数没有arguments,但可以使用rest参数;

​ (3)一般函数可以当作构造函数,箭头函数不能当作构造函数,因为箭头函数没有自己的this;

2)箭头函数简写形式:

​ (1)如果箭头函数只有一个形参时可以省略圆括号;

​ (2)如果箭头函数体只有一条语句并且该语句会作为返回值返回时,可以省略花括号及return关键词;

<script>
        // var demo = (x) => {
        //         return x * 9;
        //     }
        //等价于上面的定义:
        var demo = x => x * 9;


        console.log(demo(11));
    </script>

3)箭头函数中的this依赖外层函数中的this指向,也就是说箭头函数中的this指向定义时所在的对象,而不绑定当前调用者;

  1. 箭头函数不适用的场景:(由于this指向的问题,不能方便的取到想要的值)

​ (1)、对象中的方法不使用箭头函数

​ (2)、给dom绑定事件时不适用箭头函数

 <script>
        function fn() {
            console.log(this, 6666);
            setTimeout(function() {
                console.log(this, 111);
            }, 1000);

            setTimeout(() => {
                console.log(this, 222);
            }, 2000);
        }

        //  fn();

        //   new fn();


        var usr = "lisi";
        var obj = {
            usr: "zhangsan",
            fn: function() {
                console.log(this, 999);
                setTimeout(function() {
                    console.log(this.usr, 666);//lisi 普通的对象函数的this指向调用对象的对象(这里是windows)
                }, 1000);

                setTimeout(() => {
                    console.log(this.usr, 777);//zhangsan 箭头函数的this指向根据他的上级函数来决定
                }, 2000);
            }
        };
        /// obj.fn();


        var brand = "华为";
        var obj2 = {
                brand: "联想",
                fn: () => {
                    console.log(this, 888);
                    setTimeout(function() {
                        console.log(this.brand, 1111);
                    }, 1000);

                    setTimeout(() => {
                        console.log(this.brand, 1111);
                    }, 1000);
                }
            }
            //  obj2.fn();

        var test = () => {
            setTimeout(() => {
                console.log(this, 999);
            }, 1000);
        }

        // test();
        // test.call(new Object());

        function test2() {
            console.log(this, 777);
            setTimeout(() => {
                console.log(this);
            }, 1000);
        }
        test2.call(new Object());
    </script>

作业:参考着上面的例子定义:

1)当外层是一般函数、内层也是一般函数,

2)当外层是箭头函数、内层是一般函数,

类似于这样:

<script> 

      var demo = () => {

            console.log(this);

            setTimeout(() => {

                console.log(this);

            }, 2000);

        }

 

        demo.call(new Object());

    </script>

什么是Symbol

是es6 新增的数据类型,和原有的数据类型(String、Number、Boolean、Object、null、undefined)一样。

格式: let s = Symbol(‘描述’);

Symbol类型的特点:

​ 唯一性、不能参与运算(包括字符串拼接)、for…in或for…of不能遍历

<script>
        //Symbol是es6中新增的数据类型,主要用来产生唯一值
        let s1 = Symbol();
        let s2 = Symbol();
        console.log(s1 === s2); //false

        //Symbol的特点:
        //唯一性、不能被遍历、不能参与运算
        //  s1 + 'ok';    //不能参与运算


        //Symbol应用:
        let brand = Symbol('品牌');
        let brand2 = Symbol('品牌2');
        //  console.log(bd);
        let obj = {
            brand: '苹果'
        };

        obj.brand = '梨';
        obj[brand] = '联想';
        obj[brand2] = '华为';
        console.log(obj)//{brand: "梨", Symbol(品牌): "联想", Symbol(品牌2): "华为"}
        console.log(obj.brand);//‘梨’

        //遍历对象:
        for (var ind in obj) {
            console.log(obj[ind]);//'梨' 只能遍历到对象本身的属性,不能遍历到Symbol数据
        }
    </script>

Symbol应用:

Promise

Promise用途: 主要用来解决异步编程中数据传递的问题(可以解决回调地狱问题)

什么是同步与异步?

什么是同步?

​ 同步就是当调用发生之后在没有得到结果之前,后续代码不能正常执行。

​ 什么是异步?
​ 异步就是发调用发生之后在没有得到结果之前,后续代码可以正常执行,而这次调用结果会以通知或回调函数来告诉调用者。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qg2tIlt4-1615858418516)(C:\Users\Shinelon\AppData\Roaming\Typora\typora-user-images\1602249301444.png)]

1、 异步编程中的数据传递:

2、 在es5中使用回调函数可以解决异步编程中数据传递问题,但当业务比较复杂时代码的可读性、可维护性就变差。

3、 什么是Promise?

Promise实际上是一个许诺器,而promise里面的代码通常是将来要执行的代码,而这些将来要执行的代码一般都是异步的I[input输入]/O[output输出]操作(比如:操作文件、操作数据库),这些操作执行完成后会有两种结果:成功或失败,因此promise有三种状态:resolve(成功)、rejected(失败)、pending(初始)

1、 Promise的基本用法:

 let prm = new Promise((resolve,reject)=>{
            if(0){
                resolve('hellow');//成功执行的函数,括号中为实参
            }else{
                reject('erro');//失败执行的函数,括号中为实参
            }
        });
		//then中两个函数 第一个函数为成功时执行的函数,第二个函数为失败时执行的函数
        prm.then((d)=>{
            console.log(d);
        },(e)=>{
            console.log(e);
        })

2、 Promise异常处理:

如果把失败时的回调函数定义在then()方法中的第二个参数中,那么可以捕获到Promise里面的代码异常,但不能捕获到then()中第一个函数中的成功时回调函数里面的代码异常;如果把失败时的回调函数定义在catch()方法中,不仅可以捕获到Promise里面的代码异常,还可以捕获到then()中第一个函数中的成功时回调函数里面的代码异常。

    let prm = new Promise((resolve, reject) => {
            //将来要执行的代码 
            setTimeout(() => {
                if (1) {
                    try {
                        // fn();
                        resolve();
                    } catch (error) {
                        //  console.log(error, 999);
                        reject(error);
                    }

                } else {
                    reject();
                }
            }, 1000);

        });

        prm.then(function(d) {
            try {
                fn();
            } catch (error) {
                console.log(error);
            }

            console.log('OK');
        }, function(e) {
            console.log('NO', e);
        });

       // 注意:promise的then()调用完成后返回的是Promise对象
        console.log(prm.then());
        prm.then(function() {
            console.log('Yes');
        }).catch(function() {
            console.log('Error');
        });
        //=====================================================================


        let prm2 = new Promise((resolve, reject) => {
            //  tst();
            if (1) {
                resolve();
            } else {
                reject();
            }

        });


        prm2.then(function(d) {
            tst();
            console.log('ok', 666);
        }).catch(function(e) {
            console.log(e, 'no', 9998);
        });
    </script>

5、 Promise的链式操作:

promise对象的then()方法返回的是Promise对象

1、当在promise内部调用了resolve()方法则触动的是promise对象的第一个then()对应的成功时回调函数,该成功时回调函数执行完成后默认是resolve状态,接着后面每个then()方法的成功时回调函数都被触发

2、如果在promise内部调用了reject()方法时则触发第一个catch()对应的失败回调函数或then()方法中第二个参数对应的失败回调函数,而不会触发后面的catch()方法或then()方法中第二个参数对应的失败回调函数

 <script>
        function demo() {
            let p = new Promise((resolve, reject) => {
                // resolve('Hello');
                reject('error msg...');
            });
            return p;
        }

        //在promise内部调用了成功时的回调方法resolve(),则触发第一个then()中的成功时的回调方法,该回调方法执行完成后返回的是成功状态(resolve),因此后面每个then()中的成功时回调方法都被触发;
        // demo().then((d) => {
        //     console.log(d, 111);
        // }).then((d) => {
        //     console.log(d, 222);
        // }).then((d) => {
        //     console.log(d, 333);
        // }).catch((e) => {
        //     console.log(e, 'error...');
        // });


        //在promise内部调用了失败时的回调方法reject(),则触发第一个catch()方法或then()方法定义的失败时的回调方法,该回调方法执行完成后状态为结束,因此后面定义的catch()方法或then()中定义的失败时的回调方法不能被触发。
        demo().then((d) => {
            console.log(d, 333);
        }).catch((e) => {
            console.log(e, 666);
        }).catch((e) => {
            console.log(e, 777);
        }).catch((e) => {
            console.log(e, 888);
        });//这种写法实际上没有什么意义
    </script>

3,应用(解决回调地狱问题)

<script>
        //需求:Promise+ajax解决回调地狱问题

        //封装函数:发送ajax请求
        function sendAjax(url, data = {}, type = 'get', dataType = 'json') {

            return new Promise((resolve, reject) => {
                $.ajax({
                    url,
                    data,
                    type,
                    dataType,
                    success: function(d) {
                        // console.log(d, 999);
                        resolve(d);
                    },
                    error: function(e) {
                        //  console.log(e, 777);
                        reject(e);
                    }
                });

            });

        }

        let prm = sendAjax('http://www.ujiuye.tech:3000/api/category/first');
        prm.then((data) => {
            //  console.log(data, 111);
            //根据某个一级分类id查询其下面的所有二级分类数据:
            return sendAjax('http://www.ujiuye.tech:3000/api/category/second', {
                id: data['result']['data'][2]['first_id']
            });
        }).then((data) => {
            // console.log(data, 222);
            return sendAjax('http://www.ujiuye.tech:3000/api/category/third', {
                id: data['result']['data'][1]['second_id']
            });
        }).then((data) => {
            console.log(data, 555);
        }).catch((err) => {
            console.log(err, 333);
        });
    </script>

4,Promise.all()并发方法的特点:

​ 1、要么所有Promise实例都成功、要么都失败,

​ 2、如果都成功则返回所有Promise实例结果,并且该结果是数组,数组中的元素与all()方法中Promise实例一一对应;

​ 3、如果有一个Promise实例执行失败则都失败,并且只返回最先失败的那个Promise实例结果。

<script>
        //Promise.all([promise实例1,promise实例2...])并发的特点:
        //1、Promise.all()中的promise实例都执行成功时返回的结果为数组,数组中的元素值对应all()方法中的promise实例;
        //2、Promise.all()中的promise实例执行失败则返回最先失败的那个promise实例结果;


        function demo() {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('demo1');
                    // reject('failed1');
                }, 1000);
            });
        }


        function demo2() {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('demo2');
                    // reject('failed2');
                }, 2000);
            });
        }

        let prm = Promise.all([demo2(), demo()]);
        // console.log(prm);
        prm.then((d) => {
            console.log(d, 999);
        }).catch((e) => {
            console.log(e);
        });
</script>

5.Promise.race()方法的特点:

哪个promise实例最先执行完成返回哪个promise的结果,不管这个结果是resolve还是reject,只比谁最快。

     <script>
  //Promise.race([promise实例1,promise实例2...])[比赛]方法的特点:

        //Promise.race()强调的是速度,返回的是最先执行完成的promise实例结果,那怕这个promise实例是失败的。

        function test() {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('test111');
                    reject('failed1111...');
                }, 2000);
            });
        }

        function test2() {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    // resolve('test2222');
                    reject('failed2222...');
                }, 1000);
            });
        }

        let prm2 = Promise.race([test(), test2()]);

        prm2.then((d) => {
            console.log(d);
        }).catch((e) => {
            console.log(e);
        });
    </script>

什么是iterator?

​ Iterator中文为迭代器或遍历器,它是一种接口机制,只要提供了这种接口机制的数据类型都可以使用for…of来遍历,这些数据类型都可以使用for…of来遍历:Array、String、arguments、Set、map.

数组原来可以使用for或forEach来遍历;

对象原来可以使用for…in来遍历;

Set、map原来可以使用forEach来遍历;

<script>
        //什么是iterator接口机制?
        //iterator(遍历器或迭代器),它提供了一种接口机制,只要提供了这种接口机制的数据类型都可以统一使用for..of来遍历,这些数据类型提供了iterator接口机制【重点】:Array、String、arguments、set、map; 

        let arr = [1, 3, 5, 6];
        // console.log(arr);
        //直接使用iterator接口[了解]:
        let iteobj = arr[Symbol.iterator]();
        // console.log(iteobj);
        console.log(iteobj.next());
        console.log(iteobj.next());
        console.log(iteobj.next());
        console.log(iteobj.next());
        console.log(iteobj.next());

        //iterator接口的语法糖:for...of【用法】

        for (var x of arr) {
            if (x > 3) {
                break;
            }
            console.log(x);

        }
</script>


1,Iterator接口使用:

使用代码演示iterator接口工作过程(了解):

<script> 

        function ite() {
            var ind = 0; //下标
            return {
                next: function() {

                    if (ind < arr.length) {
                        return {
                            value: arr[ind++],
                            done: false
                        };
                    } else {
                        return {
                            value: undefined,
                            done: true
                        };
                    }
                }
            };
        }

        let obj = ite(); //返回对象
        //  console.log(obj);
        console.log(obj.next());
        console.log(obj.next());
        console.log(obj.next());
        console.log(obj.next());
        console.log(obj.next());
    </script>

2,for…of与forEach、for…in的区别?

1、for…of与forEach的区别?

forEach中不能使用break、continue,但for…of中则可以使用。

2、for…of与for…in的区别?

​ for…in通常遍历对象,for…of可以遍历数组、string、arguments、set、map.

3,给指定的数据类型部署iterator接口:

    <script>
        let obj = {
            "uid": 345,
            "usr": "Tom",
            "tel": 110
        };

        //对象不能直接使用for...of来遍历
        // for (var x of obj) {
        //     console.log(x);
        // }

        //给指定的数据类型部[对象]署iterator接口【重点】:
        obj[Symbol.iterator] = function() {
            //获取对象中所有属性名
            let karr = Object.keys(obj);
            //下标
            let ind = 0;
            // console.log(karr);
            return {

                next: function() {

                    if (ind < karr.length) {
                        let curkey = karr[ind++];
                        // console.log('curkey=' + curkey);
                        return {
                            value: obj[curkey],
                            done: false
                        };
                    } else {
                        return {
                            value: undefined,
                            done: true
                        };
                    }


                }
            };
        }

        // let objIterator = obj[Symbol.iterator]();
        // //  console.log(objIterator);
        // console.log(objIterator.next());
        // console.log(objIterator.next());
        // console.log(objIterator.next());
        // console.log(objIterator.next());

        for (var y of obj) {
            console.log(y);
        }
    </script>

Generator函数

1、 什么是generator?

​ Generator是一个函数,在内部可以定义多个不同状态,因此generator也称为状态机,它是异步编程解决方案之一。

2、Generator语法:

function* 函数名() {

yield 异步操作1;

yield 异步操作2;

}

3、Generator函数特点:

function 与函数名之间有一个星号

​ 内部用yield表达式来定义不同的状态

​ generator函数返回的是iterator对象,而不会执行函数内部逻辑

​ 调用next方法函数内部逻辑开始执行,遇到yield表达式停止,返回{value: yield后的表达式结果/undefined, done: false/true}

​ 再次调用next方法会从上一次停止时的yield处开始,直到最后

​ yield语句返回结果通常为undefined

****注意****:当给next()方法传参时会作为重启yield语句的返回值。

    <script>
        //什么是generator函数:
        //generator是一个函数,可以在函数定义多个不同状态,因此generator也称为状态机,它是解决异步编程解决方案之一。

        //generator函数语法:
        // function*   函数名(){
        //     yield 异步操作1;
        //     yield 异步操作2; 
        // }
        function* test() {
            console.log('开始执行');
            let data = yield 'Hello';
            console.log(data, '执行到这里');
            data = yield 'OK';
            console.log(data, '执行完成...');
        }


        //generator函数特点:
        //1、调用generator函数返回的是iterator对象,但函数体不会自动执行,需要手动调用next()方法 ;
        //2、调用next()方法执行generator函数体,但遇到yield关键词会停止后续代码执行;
        //3、next()方法调用返回的是对象{value:?,done:true/false}
        //4、当给next()方法传参时会作为重启yield语句的返回值[重点]

        let iteratorObj = test();
        let d1 = iteratorObj.next();
        console.log(d1);
        d1 = iteratorObj.next(666);
        console.log(d1);
        d1 = iteratorObj.next(999);
        console.log(d1);
//=======================================================================================


        function* genera() {
            console.log('开始...');
            let d = yield mydemo();
            console.log(d, '完成...');//'Hi web 完成...'
        }


        function mydemo() {
            setTimeout(() => {
                geneObj.next('Hi web');
                // console.log('Hi web');
            }, 1000);
        }

        let geneObj = genera();
        geneObj.next();
    </script>

4、Generator应用:

    <script>
        //需求:(generator+ajax)获取所有轮播图片并渲染到页面上

        //发送ajax请求
        function sendAjax({
            url,
            data = {},
            dataType = 'json'
        } = {}) {
            // $.get("", {}, function(d) {}, 'json');
            $.get(url, data, function(d) {
                // console.log(d, 5555);
                gene.next(d);
            }, dataType);
        }


        // sendAjax({
        //     url: 'http://www.ujiuye.tech:3000/api/banner'
        // });

        function* getBanners() {
            //1、获取所有轮播图片
            let {
                result//获取到的数据是个对象通过对象解构赋值找到需要的数据
            } = yield sendAjax({
                url: 'http://www.ujiuye.tech:3000/api/banner'
            });
            console.log(result, 777);
            //2、渲染轮播图片到页面上
            showdata(result);
        }

        //渲染轮播图片
        function showdata(banners) {
            let {
                data
            } = banners;
            let imgs = '';
            for (let obj of data) {
                // console.log(obj, 888);
                imgs += `<li><img src='${obj.coverimg}'></li>`;
            }
            $("#bnners").html(imgs);

        }

        let gene = getBanners();
        gene.next();
    </script>

async函数

1、async函数简介:

​ async函数是一个基于Promise的generator语法糖,以同步流程表达异步操作。

2、async函数语法:

async function 函数名(){

await 异步操作1;

await 异步操作2;

}

3、async的特点:

3.1 被async修饰过的函数返回的是Promise对象,但不需要在async函数内部手动调用成功时的回调函数或失败时的回调函数,Promise根据当前状态来自动调用;

3.2 async取代Generator函数的星号*,await取代Generator的yield;

3.3不需要像Generator去调用next方法,遇到await等待,当前的异步操作完成就往下执行;

    <script>
        async function demo() {
            //不需要在async函数内部手动调用成功时的回调函数或失败时的回调函数
            // resolve();
            //  yesok();

            return 'Hello...';

        }

        //被async修饰过的函数返回的是Promise对象;
        // console.log(demo());

        let prm = demo();
        prm.then((d) => {
            console.log(d, 111);
        }).catch((e) => {
            console.log(e, 222);
        });
    </script>

4、await的特点:

4.1 await要与async一起使用;

4.2 await通常等待的是Promise结果,当Promise结果为resolve()时await可以正常处理,但当Promise结果为reject()时await不能正常处理,我们可以使用这两种方法来处理Promise的异常:第一种为直接使用promise对象的catch()方法进行错误处理,但代码可读性太差;第二种为不管Promise内部代码执行成功或失败都调用成功时的回调函数,但要注意调用成功时的回调函数时要传组:resolve([,]);

4.3 await后面也可以跟同步操作,但很少这样使用;

    <script>
        // function demo() {
        //     setTimeout(() => {
        //         //   console.log('Hello  WEB!');
        //     }, 1000);
        // }

        function demo() {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    //   console.log('Hello  WEB!');
                    if (0) {
                        resolve([null, 'Hello WEB!']);
                    } else {
                        //  reject('error message');
                        resolve(['error message', '']);
                    }
                }, 1000);
            });


        }

        //await特点:
        //1、await不能单独使用,必须和async一起使用
        //2、await通常等待的是Promise的结果,await可以直接处理promise成功时的结果,await对于promise失败时的结果不能直接处理,解决办法有两种:
        //   第一种:在promise对象的catch()方法进行错误处理,但代码可读性太差;
        //   第二种:不管Promise内部代码执行成功或失败都调用成功时的回调函数,但要注意调用成功时的回调函数时要传数组:resolve([,])
        async function fn() {
            console.log('开始...');
            let d1 = await 'ok';
            console.log(d1, '执行到这里');
            //第一种解决方法:
            // d1 = await demo().catch((e) => {
            //     console.log(e, 9999);
            // });
            d1 = await demo();
            if (d1[0] === null) { //成功
                console.log('成功:' + d1[1]);
            } else { //失败
                console.log('失败:' + d1[0]);
            }
            console.log(d1, '完成....');
        }

        fn();
    </script>

5、async函数应用(解决回调地狱问题):

   <script src="../day02/jquery.js"></script>
    <script>
        //使用async...await+Promise解决回调地狱问题:

        //定义函数:发送ajax请求
        function sendAjax({
            url,
            data = {},
            type = 'get',
            dataType = 'json'
        }) {

            return new Promise((resolve, reject) => {
                $.ajax({
                    url,
                    data,
                    type,
                    dataType,
                    success: function(d) {
                        resolve([null, d]);
                        // console.log(d, 111);
                    },
                    error: function(e) {
                        // console.log(e, 222);
                        resolve([e]);
                    }
                });
            });


        }


        async function getCateinfo() {
            //获取所有一级商品分类
            let [err, obj] = await sendAjax({
                url: 'http://www.ujiuye.tech:3000/api/category/first'
            });
            // console.log(obj, 999);

            //根据某个一级商品分类获取其下面的所有二级分类 
            [err, obj] = await sendAjax({
                url: 'http://www.ujiuye.tech:3000/api/category/second',
                data: {
                    id: obj['result']['data'][3]['first_id']
                }
            });
            //  console.log(obj, 7777);

            [err, obj] = await sendAjax({
                url: 'http://www.ujiuye.tech:3000/api/category/third',
                data: {
                    id: obj['result']['data'][0]['second_id']
                }
            });

            console.log(obj, 6666);
        }

        getCateinfo();
    </script>

作业: 将小U商城首页中的轮播图片使用async…await + Promise调用接口程序改为动态的

类:

1、面向对象与面向过程:

​ 面向过程(吃蛋炒饭):1)买食材 --> 2)洗食材 --> 3)做饭 --> 4)吃蛋炒饭

​ 面向对象(吃蛋炒饭):1)打开app -->2)选择商家并下单 -->3)外卖小哥送餐 -->4)吃蛋炒饭

​ 面向对象编程可以提高代码重用度、降低系统耦合性;

​ 不同编程语言实现面向对象时的方式不一样,比如:像java、php等语言采用的"类"这种方式来实现的,而javascript语言采用的“原型”这种方式来实现的。

2、什么是类与对象?

​ 2.1、什么是类?

​ 类是具有相同属性和方法(行为)的一类事务的集合,比如:人类、电脑等;

​ 2.2、什么是对象?

​ 类中某个具体的个体称为对象,比如:张三这个人,你的电脑等;

3、类的语法:

​ class 类名{

​ //属性

​ //方法

​ }

4、类的基本用法:

    <script>
        //定义类[人类]:
        class Peoples {
            //属性
            // 注意:定义属性时前面不要加let 或 var
            names; //姓名
            height; //身高


            //方法
            //注意:定义方法时不要加function
            eat() {
                console.log(`姓名:${this.names} 身高:${this.height} 正在吃饭...`);
            }

            work() {
                console.log(`姓名:${this.names} 身高:${this.height} 正在工作!`);
            }
        }

        //实例化类[对象]:
        let lisi = new Peoples();
        // console.log(lisi);
        lisi.names = '李四';
        lisi.height = 180;
        lisi.eat();
        lisi.work();

        let zhangsan = new Peoples();
        zhangsan.names = '张三';
        zhangsan.height = 185;
        zhangsan.work();
    </script>

5、类的构造方法:

构造方法在类实例化时会被自动调用,通常用来作初始化使用

    <script>
        //定义类[人类]:   
        class Peoples {
            //属性
            names;
            height;

            //方法
            //注意:构造方法在类实例化时会被自动调用,通常用来作初始化使用;
            constructor(n, h) {
                // console.log(`${this.names}Hello...`);
                this.names = n;
                this.height = h;
            }

            eat() {
                console.log(`姓名:${this.names} 身高:${this.height} 正在吃饭`);
            }
        }

        //实例化类[对象]:
        let lisi = new Peoples('李四', 180);
        lisi.eat();

        let zhangsan = new Peoples('张三', 185);
        zhangsan.eat();
    </script>

6、类的成员属性、方法与静态属性、方法:

​ 6.1、类的成员属性、方法?

​ 在创建对象时会把类中的属性和方法拷贝一份,每个对象都具有类中的属性和方法,这要的属性和方法称为成员属性、方法,成员属性、方法属于对象而不属于类;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FmM9lImD-1615858418525)(C:\Users\ibm\AppData\Roaming\Typora\typora-user-images\image-20201012163419741.png)]

​ 6.2、类的静态属性、方法?

​ 静态属性、方法归类所有, 全局独一份,在静态方法中只能访问静态属性,当然在成员方法中只能访问成员属性,静态方法直接通过类名来访问,不需要实例化。

    <script>
        //定义类[人类]:   
        class Peoples {
            //属性
            names;
            height;

            //静态属性:
            static type = '人类';

            //方法
            //注意:构造方法在类实例化时会被自动调用,通常用来作初始化使用;
            constructor(n, h) {
                // console.log(`${this.names}Hello...`);
                this.names = n;
                this.height = h;
            }

            //定义静态方法:
            static gettypes() {
                console.log(`类别:${this.type}  姓名:${this.names}`);
            }

            eat() { //成员方法
                //注意: 成员方法不能访问静态属性,只能访问成员属性
                console.log(this.type, 999);
                console.log(`姓名:${this.names} 身高:${this.height} 正在吃饭`);
            }
        }

        //实例化类[对象]:
        let lisi = new Peoples('李四', 180);
        lisi.eat();

        let zhangsan = new Peoples('张三', 185);
        zhangsan.eat();

        //注意:静态方法不能通过对象来访问,只能通过类名.静态方法名()来访问 
        // lisi.gettypes();
        Peoples.gettypes();
    </script>


6.4、静态属性、方法应用:

    <script>
        //需求:创建新闻类别的下拉框
        class NewsType {
            //新闻类别:
            static types = ['娱乐', '社会', '军事', '体育'];

            //创建新闻类别的下拉框
            static createopt() {
                let arr = this.types;
                document.write("<select>");
                arr.forEach((item) => {
                    document.write(`<option>${item}</option>`);
                });
                document.write("</select>");
            }
        }

        NewsType.createopt();
    </script>


7、类的继承:extends

​ 7.1、通过类的继承可以把父类中的基本属性、方法继承下来(包括静态属性及方法);

​ 7.2、当子类没有定义构造 方法时默认访问父类的构造方法,当子类定义了构造方法则访问子类自己的构造方法;

​ 7.3、如果子类中定义了构造方法,则需要调用super()方法,通过super()方法可以把父类中的this继承下来的同时也可以给父类中的属性设置属性值

​ 7.4、super既可以是super()方法也可以是super对象,super当对象使用时可以直接通过super(相当于this).父类中的方法名()

​ 7.5、方法重构/重写:

为什么要进行方法重构/重写?

​ 当父类中的方法在子类中满足不了需求时则需要对父类中的方法进行重构/重写。

​ 怎样进行方法重构/重写?

​ 在子类中定义和父类同名的方法即可。

    <script>
        //定义类[人类]:   
        class Peoples {
            //属性
            names;
            height;

            //静态属性:
            static type = '人类';

            //方法
            //注意:构造方法在类实例化时会被自动调用,通常用来作初始化使用;
            constructor(n, h) {
                // console.log(`${this.names}Hello...`);
                this.names = n;
                this.height = h;
            }

            //定义静态方法:
            static gettypes() {
                console.log(`类别:${this.type}`);
            }

            eat() { //成员方法
                console.log(`姓名:${this.names} 身高:${this.height} 正在地吃饭`);
            }
        }

        /*
                //定义类[学生类]
                class Student extends Peoples {

                }

                //注意:当子类没有定义构造 方法时默认访问父类的构造方法;
                let xiaoming = new Student('小明', 170);
                xiaoming.eat();
            */

        //定义类[工人类]
        class Worker extends Peoples {

        }

        let tom = new Worker();
        // tom.eat();


        //定义类[学生类]
        class Student extends Peoples {
            xuehao; // 学号

            constructor(xh, name, heights) {
                super(name, heights);
                this.xuehao = xh;
                // console.log(this.names, 'stu....');
            }

            homework() {
                console.log(`学号:${this.xuehao} 姓名:${this.names} 身高:${this.height}正在写作业...`);
            }

            demo() { //super当对象使用时直接访问父类中的方法eat()
                super.eat();
                this.eat();
                console.log(`姓名:${super.names}  ${this.names}`);
            }

            //方法重构
            eat() {
                console.log(`学号:${this.xuehao} 姓名:${this.names} 正在大口大口地吃饭....`);
            }
        }

        let xiaoming = new Student('xm124', '小明', 170);
        xiaoming.eat();
        xiaoming.homework();

        //访问父类中的静态方法:
        // Student.gettypes();

        //  xiaoming.demo();
    </script>


8、类的应用:

需求:使用类、async…await、Promise+ajax技术调用接口:http://www.ujiuye.tech:3000/api/category/first,所有一级分类展示到界面上。

    <script>
        // 需求:使用类、async...await、Promise+ajax技术调用接口:http://www.ujiuye.tech:3000/api/category/first,所有一级分类展示到界面上。

        class CateData {
            //属性
            url; //url地址
            data; //要发送的参数
            dataType; //期望服务器返回的数据类型

            //方法

            constructor(urls, datas = {}, dataTypes = 'json') {
                this.url = urls;
                this.data = datas;
                this.dataType = dataTypes;
            }

            //发送ajax请求的
            sendAjax(url) {

                return new Promise((resolve, reject) => {
                    $.get(this.url, this.data, function(d) {
                        if (d.code == 200) { //成功
                            resolve([null, d.result]);
                        } else { //失败
                            resolve([d.msg]);
                        }
                    }, this.dataType);
                });

            }

            //渲染数据到页面
            async renderData() {
                let [err, obj] = await this.sendAjax();
                //  console.log(err, data);
                let arr = obj.data;
                let divs = '';
                arr.forEach((item) => {
                    divs += `<li>${item.first_name}</li>`;
                });
                $("#cates").html(divs);
            }
        }

        let obj = new CateData('http://www.ujiuye.tech:3000/api/category/first');

        // obj.url = 'yesok';
        // console.log(obj.url);

        $(function() {
            obj.renderData();
        })

    </script>
</head>

<body>
    <div id="cates"></div>
</body>

set:

​ set与map是es6新增的数据结构,它们对应ES5中的Array和Object数据结构。

1、什么是set?

​ Set类似于数组,但与数组不同的是set中的成员没有重复的。

2、set的使用:

set原型提供了这些方法:add()、delete()、has()、clear()、 size

    <script>
        let sets = new Set([1, 3, 5, 3, 6, 7, 9, 5]);   //定义set【重点】
        console.log(sets);
        sets.add(100); //添加
        console.log(sets);
        sets.delete(100); //删除
        console.log(sets.has(100));


        //set应用[数组去重]【重点】:

        let arr = [9, 6, 7, 3, 7, 3, 1];
        let curset = new Set(arr);
        console.log(curset);
        //将set类型转换成真正的数组
        let arr2 = Array.from(curset);
        console.log(arr2);
    </script>

map:

1、什么是map?

map类似于对象,但与对象不同的是对象中的键名只能为字符串,而map中的键名可以是任意类型【重点】

2、map使用:

map原型提供了一些方法: set()、get()、delete()、has()、clear()、size

    <script>
        let curmap = new Map();

        curmap.set(6, 'ok');
        let obj = new Object();
        curmap.set(obj, {
            x: 1,
            y: 2
        });
        console.log(curmap, curmap.get(obj));
    </script>

深拷贝与浅拷贝:

1、简单数据类型与复杂数据类型:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pID7RyOW-1615858418529)(D:\beijing1009\day04\笔记\基本数据类型与引用数据类型.png)]

2、什么是浅拷贝?【重点】

只拷贝指向某个对象的内存地址,两个对象共用一个内存空间,改变其中一个对象的值,另一个对象也跟着改变。

    <script>
        //简单数据类型:
        let x = 33;
        let y = x;
        y = 66;
        // console.log(x, y);

        //复杂数据类型:
        let obj = {
            t: 11,
            s: 8
        };
        let obj2 = obj;
        obj2.s = 99;
        console.log(obj, obj2);
    </script>

3、什么是深拷贝?【重点】

拷贝并新创建对象,重新分配内存空间,两个对象不共用一个内存空间,改变一个对象的值不会影响另外一个对象。

    <script>
        //简单数据类型:
        let x = 33;
        let y = x;
        y = 66;
        // console.log(x, y);

        //复杂数据类型:
        let obj = {
            t: 11,
            s: 8,
            fn: function() {
                console.log('test');
            }
        };
        let obj2 = obj;
        obj2.s = 99;
        console.log(obj, obj2);


        //深拷贝:JSON.parse(JSON.stringify())
        //注意:如果对象中的属性值为函数时则不能拷贝
        let obj3 = JSON.parse(JSON.stringify(obj));
        obj3.s = 100;
        //  console.log(obj, obj3);

        let obj4 = deepCopy(obj);
        console.log(obj, obj4);

        //浅拷贝+递归函数:
        function deepCopy(obj) {
            if (Object.prototype.toString.call(obj).slice(8, -1) == 'Object') {
                var result = {}
            } else if (Object.prototype.toString.call(obj).slice(8, -1) == 'Array') {
                var result = []
            } //判断数据类型类型  如果是数组则初始一个  []  如果是一个Object则初始一个 {}


            //浅拷贝,但是+ 递归思想,就实现了深拷贝
            for (var attr in obj) {
                if (typeof obj[attr] == 'object') {
                    result[attr] = deepCopy(obj[attr])
                } else {
                    result[attr] = obj[attr]
                }
            }
            return result
        }
    </script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值