2023/3/1日笔记

205 阅读3分钟

2-5、对象的深克隆

对象的深克隆

  • 复习什么是深克隆:克隆对象的全貌,不论对象的属性值是否又是引用类型值,都能将它们实现克隆
  • 和数组的深克隆类似,对象的深克隆需要使用递归
  • 面试时经常会考察深克隆算法,必须掌握

代码案例

<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2-5、对象的深克隆</title>
</head>

<body>
    <script>
        var obj1 = {
            a: 1,
            b: 2,
            c: [33, 44, {
                m: 55,
                n: 66,
                p: [77, 88]
            }]
        }

        // 深克隆
        function deepClone(o) {
            // 判断o是对象还是数组
            if (Array.isArray(o)) {
                // 数组
                var result = [];
                for (var i = 0; i < o.length; i++) {
                    result.push(deepClone(o[i]));
                }
            } else if (typeof o == "object") {
                // 对象
                var result = {};
                for (var k in o) {
                    result[k] = deepClone(o[k]);
                }
            } else {
                // 基本类型值
                var result = o
            }
            return result;
        }


        var obj2 = deepClone(obj1);
        console.log(obj2);

        console.log(obj1.c == obj2.c);      // false

        obj1.c.push(99);        
        
        console.log(obj1);
        console.log(obj2);      // obj2不变的,因为没有“藕断丝连”的现象

        obj1.c[2].p.push(999);

        console.log(obj1);
        console.log(obj2);      // obj2不变的,因为没有“藕断丝连”的现象
    </script>
</body>

</html>

2-6、认识上下文

函数的上下文

  • 函数中可以使用this关键字,它表示函数的上下文
  • 与中文中“这”类似,函数中的this具体指代什么必须通过调用函数时的“前言后语”来判断

函数中的this

image.png

函数的上下文由调用方式决定

  • 同一个函数,用不同的形式调用它,则函数的上下文不同

image.png

    • 函数只有被调用,它的上下文才能被确定 image.png

代码案例

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2-6、认识上下文</title>
</head>
<body>
    <script>
        var obj = {
            a: 1,
            b: 2,
            fn: function () {
                console.log(this.a + this.b);
                console.log(this === window);
                console.log(this === obj);
            }
        }

        // obj.fn();       //这个时候的this代表obj对象,所以输出的结果会是3

        // var fn = obj.fn();      // 如果这个fn加了括号,将是直接调用函数,而不是赋值,变量fn代表的就是undefined
        var fn = obj.fn;        // 这个时候的fn已经被单独提了出来,此时再去调用时this将代表window对象,而window对象的所有属性都是全局变量
        fn()        // NaN

        // 假设这个时候刚好有全局变量,那么这时的this.a将会调用全局变量a
        var a = 10;
        var b = 5;

        fn();       // 15
    </script>
</body>
</html>

2-7、上下文规则1

函数的上下文由调用函数的方式决定

  • 函数的上下文(this关键字)由调用函数的方式决定,function是“运行时上下文”策略
  • 函数如果不调用,则不能确定函数的上下文

规则一

  • 规则1:对象打点调用它的方法函数,则函数的上下文是这个打点的对象

image.png

规则1题目举例 - 第1小题

image.png

规则1题目举例 - 第2小题

image.png

规则1题目举例 - 第3小题

image.png

规则1题目举例 - 第4小题

image.png

代码案例

<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2-7、上下文规则1</title>
</head>

<body>
    <script>
        // 案例1
        function fn () {
            console.log(this.a + this.b);
        }
        var obj = {
            a: 1,
            b: 2,
            fn: fn
        };

        obj.fn();       // 3

        // 案例2
        var obj1 = {
            a: 1,
            b: 2,
            fn: function () {
                console.log(this.a + this.b);
            }
        };

        var obj2 = {
            a: 3,
            b: 4,
            fn: obj1.fn
        };
        obj2.fn();      // 7

        // 案例3
        function outer() {
            var a = 11;
            var b =22;
            return {
                a: 33,
                b: 44,
                fn: function () {
                    console.log(this.a + this.b);
                }
            };
        }
        // 因为当调用完毕outer函数的时候就outer()已经是返回值中的那个对象了,所以此时的this指代的就是这个对象

        outer().fn();       // 77

        // 案例4
        function fun() {
            console.log(this.a + this.b);
        }
        var obj = {
            a: 1,
            b: 2,
            c: [{
                a: 3,
                b: 4,
                c: fun
            }]
        };
        
        var a = 5;
        obj.c[0].c();
    </script>
</body>

</html>

2-8、上下文规则2

规则2

  • 规则2:圆括号直接调用函数,则函数的上下文是window对象

image.png

规则2题目举例 - 第1小题

image.png

规则2题目举例 - 第2小题

image.png

代码案例

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2-8、上下文规则2</title>
</head>
<body>
    <script>
        // 案例1
        var obj1 = {
            a: 1,
            b: 2,
            fn: function() {
                console.log(this.a +this.b);
            },
        };

        var a = 3;
        var b = 4;

        var fn = obj1.fn;
        fn();       // 7

        // 案例2 
        function fun () {
            return this.a + this.b;
        }
        var a = 1;
        var b = 2;
        var obj = {
            a: 3,
            // 这里是直接调用函数,又因为这个函数中的this指代的是window对象,所以等于两个全局变量相加,等于3
            b: fun(),
            fun: fun
        };

        var result = obj.fun();     
        console.log(result);        // 6
    </script>
</body>
</html>

2-9、上下文规则3

规则3

  • 规则3:数组(类数组对象)枚举出函数进行调用,上下文是这个数组(类数组对象)

image.png

规则3题目举例 - 第1小题

image.png

类数组对象

  • 什么是类数组对象:所有键名为自然数序列(从0开始),且有length属性的对象
  • argument对象是最常见的类数组对象,它是函数的实参列表

规则3题目举例 - 第2小题

image.png

代码案例

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2-9、上下文规则3</title>
</head>
<body>
    <script>
        // 案例1
        var arr = ["A","B","C",function () {
            console.log(this[0]);
        }];
        arr[3]();       // "A"

        // 案例2
        // 这段代码的含义是声明一个函数,函数里面调用传入进来的参数的下标为3的函数
        function fun() {
            arguments[3]();
        }

        // 这段代码的含义是调用函数,实参中下标为3的地方声明了一个匿名函数,里面内容是输出this指代的fun对象中的实参下标为一的值
        fun("A","B","C",function () {
            console.log(this[1]);       // "B"
        });
    </script>
</body>
</html>

2-10、上下文规则4

规则4

  • 规则4:IIFE中的函数,上下文是window对象

image.png

规则4题目举例

image.png

代码案例

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2-10、上下文规则4</title>
</head>
<body>
    <script>
        var a = 1;
        var obj = {
            a: 2,
            // 这里的fun其实是匿名函数里面的返回值函数,因为这个匿名函数是IIFE,会直接调用并不先进入对象
            fun: (function () {
                // 这里因为是还没进入对象所以此时的this指的是window对象
                var a = this.a;
                return function () {
                    console.log(a + this.a);        // 1(全局变量) + 2(对象键)
                }
            })()
        };
        // 这里调用是用对象点函数调用的,所以返回函数中的this指代的是obj对象
        obj.fun();      // 3
    </script>
</body>
</html>

2-11、上下文规则5

规则5

  • 规则5:定时器、延时器调用函数,上下文是window对象

image.png

规则5题目举例

image.png

image.png

代码案例

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2-11、上下文规则5</title>
</head>
<body>
    <script>
        // 案例1
        var obj = {
            a: 1,
            b: 2,
            fun: function () {
                console.log(this.a + this.b);       // 7
            },
        };

        var a = 3;
        var b = 4;
        setTimeout(obj.fun,2000)        // 适用规则5,此时的this指代的是window对象

        // 案例2
        var obj = {
            a: 1,
            b: 2,
            fun: function () {
                console.log(this.a + this.b);       // 3
            },
        };

        var a = 3;
        var b = 4;
        setTimeout(function() {
            obj.fun();      // 适用规则1,而此时的this指代的就变为了obj
        },2000);
    </script>
</body>
</html>

2-12、上下文规则6

规则6

  • 规则6:事件处理函数的上下文是绑定事件的DOM元素

image.png

规则6 - 小案例1

  • 请实现效果:点击哪个盒子,哪个盒子就变红,要求使用同一个事件处理函数实现

规则6 - 小案例2

  • 请实现效果:点击哪个盒子,哪个盒子在2000毫秒后就变红,要求使用同一个事件处理函数实现

代码案例

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2-12、上下文规则6</title>
    <style>
        div {
            width: 100px;
            height: 100px;
            display: inline-block;
            border: solid 3px blue;
        }
    </style>
</head>
<body>
    <div id="box1"></div>
    <div id="box2"></div>
    <div id="box3"></div>
    <script>
        var box1 = document.getElementById("box1");
        var box2 = document.getElementById("box2");
        var box3 = document.getElementById("box3");
        
        // 案例1    点击哪个盒子哪个盒子就变红(要求:用同一个事件实现)
        
        // function setColorToRed() {
        //     if (this.style.backgroundColor == "red") {
        //         this.style.backgroundColor = "";
        //     } else {
        //         this.style.backgroundColor = "red";
        //     }
        // }

        // box1.onclick = setColorToRed;
        // box2.onclick = setColorToRed;
        // box3.onclick = setColorToRed;

        // 案例2    点击哪个盒子,哪个盒子就在2秒后变红

        function setColorToRed() {
            // 复制上下文
            var self = this;
            setTimeout(function() {
                if (self.style.backgroundColor == "red") {
                    self.style.backgroundColor = "";
                } else {
                    self.style.backgroundColor = "red";
                }
            },2000)
        }

        box1.onclick = setColorToRed;
        box2.onclick = setColorToRed;
        box3.onclick = setColorToRed;
    </script>
</body>
</html>

2-13、call和apply

call和apply能指定函数的上下文

image.png

image.png

call和apply的区别

image.png

到底使用call还是apply (apply)

image.png

上下文规则总结

image.png