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

首先,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

可以将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]();

体会以下代码:

for (let i = 0; i < 3; i++) {
   let i = 'abc';
   console.log(i);
}

// abc
// abc
// abc
输出三次abc
可以看出for循环的变量i和循环体中的i在不同作用域中。

(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:

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

在这里插入图片描述
例题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执行后其内容会被销毁。

例题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

例题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。

面试题:

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

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

普通嵌套函数:
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;

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

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属性将暴露对象给定义好了,
所以在全局作用域下,只需要调用即可。

借鉴

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值