• js的闭包例题


    1、垃圾回收
    (1)全局变量不会被回收
    (2)局部变量会被回收,函数执行结束,函数内部东西会被销毁
    (3)某作用域中的某个变量还在被另一个作用域引用就不会被回收

    var a=[];
    for(var i = 0;i<10;i++){
       var q = i;
       a[i]=function(){console.log(q)}
    }
    a[0]();//9
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    首先,for循环不是一个函数作用域,因此变量a,i,q都是同级的。其次,只是将函数赋值给a[i],函数并没有立即执行。for循环执行完毕后,i=10,q=9,因此,执行a[0](),输出qq=9
    解决方法:
    (1)使用let声明变量
    (2)使用闭包

    (1)使用let声明变量

    var a=[];
    for(let i = 0;i<10;i++){
       let q = i;
       a[i]=function(){console.log(q)}
    }
    a[6]();//6
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    可以将for循环理解为两个块级作用域:

    var a=[];
    let i=0;
    if(i<10){
    	let q = 0;
       a[0]=function(){console.log(q)}
    };
    let i=1;
    if(i<10){
    	let q = 1;
       a[1]=function(){console.log(q)}
    };
    
    ...
    
    let i=9;
    if(i<10){
    	let q = 9;
       a[9]=function(){console.log(q)}
    };
    
    let i=10;
    a[6]();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    体会以下代码:

    for (let i = 0; i < 3; i++) {
       let i = 'abc';
       console.log(i);
    }
    
    // abc
    // abc
    // abc
    输出三次abc
    可以看出for循环的变量i和循环体中的i在不同作用域中。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    (2)使用闭包
    闭包特性:
    ①函数嵌套函数
    ②内部函数可以访问外部函数的变量
    ③被访问的参数和变量不会被js垃圾回收机制回收

            var a=[];
            for (var i = 0; i < 10; i++) {
                (function (n) {
                	let q=n;
                    a[n]=function(){console.log(q)}
                    })(i);
            }
            a[6]();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    注:例题1:

    		function fc(){
                let n=1;
                function sum(){//函数声明时没有开辟内存空间
                    console.log(++n);
                }
                sum();//函数执行时,开辟内存空间压栈
            }
            fc();//2,开辟内存空间,压栈;运行完,出栈,内存被回收
            fc();//2,开辟新的内存空间,压栈,与上面开辟的内存空间是独立的,互不干涉,因此打印的都是2.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述
    例题2:

    		function fc(){
                let n=1;
                return function sum(){//返回函数的内存地址
                    console.log(++n);
                }
            }
            let a=fc();//引用函数的内存地址,因此函数fc执行完不会被销毁,会继续使用。
            a();//2
            a();//3
            let b=fc();//重新创建新的内存空间
            b();//2
            b();//3
            
    区别:
    
    		function fc(){
                let n=1;
                return n;//此处只是返回n的值,
            }
            let a=fc();//变量a只是得到n的值,因此函数fc执行后其内容会被销毁。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    例题3:

    		function fc(){
                let n=1;
                return function sum(){
                    let m=1;
                    function show(){
                        console.log(++m);
                    }
                    show();
                }
            }
            let a=fc();
            a();//2
            a();//2
            这个例题不是很理解啊!!!!
            函数sum被引用,在外部多次执行sum时,
            show函数会被多次创建新的内存空间,而不是使用原内存空间进行累加,
            为啥呀???
            
    区别以下代码:
    		function fc(){
                let n=1;
                return function sum(){//函数声明时没有开辟内存空间
                    let m=1;
                    return function show(){
                        console.log(++m);
                    }
                }
                //sum();//函数执行时,开辟内存空间压栈
            }
            let a=fc()();//此处是将show函数返回到外部
            a();//2
            a();//3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    例题4:

    		var name='The window';
            var obj={
                name:'MY OBJ',
                getNameFunc:function(){
                        return this.name;
                }
            };
            alert(obj.getNameFunc());//MY OBJ
            
    区别以下代码:
    
    		var name='The window';
            var obj={
                name:'MY OBJ',
                getNameFunc:function(){
                	return function(){
                		return this.name;
                	}
                }
            };
            alert(obj.getNameFunc()());//The window
    //obj.getNameFunc()()运行匿名函数function(){ return this.name;},this指向window。
    匿名子函数没有引用父函数getNameFunc的变量,所以没有形成闭包。
    
    区别以下代码:
    var name2 = "the window";
    var obj2 = {                            
        name2:"my name is a obj",
        getNameFunc2 :function fn1(){
            var that = this;    
            return function(){
                return that.name2;  
            };
        }
    }
    alert(obj2.getNameFunc2()());    //输出 my name is a obj
    有函数嵌套,匿名子函数引用了父函数getNameFunc的变量,产生了闭包环境。
    父函数的this指向obj2,that指向obj2。
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    面试题:

    如何理解闭包?有什么作用?如何产生闭包?需要注意什么?

    理解闭包:
    一个父函数嵌套着另一个子函数。
    正常来说,对于嵌套函数,内部的子函数不能被外部作用域引用,但是如果把这个子函数作为一个返回值传给父函数,那么作用域就能执行这个子函数内的结果了。
    闭包的作用
    相同函数可以用多个相互独立的对象引用,避免代码冗余、相互污染。
    产生闭包:
    产生闭包必须要有嵌套函数,以及子函数引用父函数的变量或属性。

    普通嵌套函数:
    function fun(){
        var a = 100;
          function fn(){
              console.log(++a);
          }
        fn();
    }
    fun();    // 101
    fun();    // 101
    fun();    // 101
    产生闭包:
     function fun(){
        var a = 20;
        return function fn(){    // 直接将执行结果返回给 fun,这个 fn就是闭包了。
           console.log(++a);
        }
    }
    var fns = fun();    // 接收函数fn
    fns()    // 输出21;
    fns()    // 输出22;
    fns()    // 输出23;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    闭包的缺点:
    函数执行完后,函数内的局部变量没有释放,占用内存时间变长,容易造成内存泄露。(当程序运行需要的内存超出了剩余内存时,就会产生内存泄露。由于设计错误,导致在释放该内存之前就失去了对该内存的控制,从而造成了内存的浪费。内存泄露过多,会影响程序性能,甚至导致程序崩溃。)
    解决:内部函数赋值为null,让浏览器回收闭包。

    闭包的应用:
    1、具有特定功能的JS模块
    2、将所有的数据和方法封装在一个函数内部

    1function fun(){    // JS 模块,并且有特定的功能,转换大小写
            var msg = "my name is a juzheng";    // 私有属性
    
            // 操作数据的函数
            function f1(){
                console.log("输出小写" + msg.toLowercase());    // 调用了上层的局部变量属性
            }
            function f2(){
                console.log("输出大写"+ msg.toUpperCase());    
            }
    
            // 向全局暴露两个对象,需要一个对象容器来保存
            return {
                one:f1,
                two:f2
            }
        }
        
    // 接收返回值
        var abc = fun();    // 通过暴露对象,接收数据
            abc.one();    // 接收容器的对象
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    2
    (function fun(window){    // JS模块,匿名函数自调用, 并且具有转换大小写功能
            var msg = "my name is a juzheng";
            
            function f1(){
                console.log("输出小写" + msg.toLowerCase());    //调用上层变量属性
            }
            function f2(){
                console.log("输出大写" + msg.toUpperCase());    
            }
    
            window.myModel = {    //将容器对象,转为 window全局对象,将容器对象暴露出来,
                one:f1,
                two:f2,
            }
    
         })(window)    // 推荐函数自调用时,形参和实参写上 window, 这样可以实现压缩代码
    
    // 调用暴露对象
        myModel.one();
        myModel.two();    
    // 不需要用一个变量来接收,因为不是用return方法,而是在闭包中用window属性将暴露对象给定义好了,
    所以在全局作用域下,只需要调用即可。
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    借鉴

  • 相关阅读:
    【leetcode】754.到达终点的数字
    apritag 定位记录 C++ opencv 3.4.5
    MiniGUI和其他嵌入式Linux 上的图形及图形用户界面系统
    find 命令 7 种高级用法
    开源语言大模型演进史:向LLaMA 2看齐
    元数据管理-解决方案调研一:元数据概述
    Redis 通用命令(keys,help,mset,exists,expire,ttl,tab补全)
    和monkey的相处日记
    基于Python实现的CNN for OxFlowers17实验
    java毕业设计疫情期间中小学生作业线上管理系统设计与实现Mybatis+系统+数据库+调试部署
  • 原文地址:https://blog.csdn.net/EmilyHoward/article/details/127881515