前端偶遇之闭包

本文深入浅出地介绍了JavaScript中的闭包概念,从变量作用域出发,详细解释了闭包的基本原理,展示了如何创建闭包来保护变量,并通过具体示例探讨了闭包在访问局部变量时的内存问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       写过一些前端页面,一直都未接触到闭包的概念,偶然接触到一种前端方法的写法

(function (n){

       //执行代码

})(i);

当下不由的好奇,这种写法究竟代表着啥意思,于是引出了一个概念,闭包!

       在介绍闭包之前,需要介绍下js中的变量的作用域.JS的变量作用域有两种,一种是全局变量,一种是局部变量,简单的总结了一下,两者在一些方面的区别

 

声明位置

访问位置

生命周期

全局变量

<script>标签下,方法外

默认变成Window的属性,当前页面的任何位置都可以访问

生命周期长,直到浏览器关闭,才会被垃圾回收

局部变量

<script>标签中的方法内

只在方法内部能访问

方法调用完成之后立即销毁

一,访问单个变量的闭包

在Java中处理一些变量,既要保证其安全性,将其私有化,又要提供公有的get方法用来对属性进行访问,这就是简单的Javabean,在JS中同样存在这种思想,也就是闭包.闭包的判别标志有两个,一个是是否封闭,例如在一个方法中定义变量,也就是局部变量,第二个是是否为其提供了方法用以访问封起来的变量.例如

<script>
    function fn() {
        //声明一个局部变量temp,并赋值1024
       
var temp = 1024;
        //提供外部访问局部变量的方法
       
return function getV(){
            return temp;
        };
    }
    //执行fn返回其闭包函数对象getV
   
var getV = fn();
    //返回的是一个方法,可执行,返回的是局部变量值temp
   
var V = getV();
    console.log(V);
</script>

结果如下:

此处可以看到,temp的值是局部函数fn中定义的,而在方法外部应是无法访问,所以提高的闭包函数.getV将其生命周期延长了,此处的作用会带来极大的内存问题,在后面解释.

二,访问多个变量的闭包实现

       上述介绍的简单的访问一个变量的闭包实现,如果返回的是多个属性,该如何?既然JS的闭包和Java中的Javabean相似,那么可以举例,如果在一个Class Student中我们需要同时取得这个学生的语文成绩,数学成绩,英语成绩该如何?一种方案是将成绩封装成一个类Grade,这个类中有chineseGrade,mathGrade,enlishGrade等属性,那么我们在Student中为Grade属性提供一个get方法即可拿到其各科成绩,在JS中同样适用这种思想,将定义在方法中的局部变量封装成一个JS对象,将这个对象返回处理,在JS中的对象可以看成一个JSON.

<script>
    function fn(){
       var chinese = 120;
       var math = 148;
       var english = 139;

       return{
          'getChinese': function getChinese() {
                return chinese;
           },
          'getMath': function getMath() {
                return math;
           },
          'getEnglish': function getEnglish() {
              returnenglish;
          }
       };
    }
    var grade = fn();
    console.log(grade.getChinese());;
    console.log(grade.getEnglish());;
    console.log(grade.getMath());;
</script>

结果如下:

当运行fn的时候返回的是一个js对象,以json的形式.

三,闭包中的重中之重—引用

*****外部函数的返回值是对内部函数的引用,内部函数的返回值是对局部变量的引用********

*****函数每执行一次则在内存中生成一个副本*****

<script>
    function fn(){
        var arr = [];
        for(var i=0;i<5;i++){
            //向数组中添加方法,返回i的平方
           
arr.push(function(){
                return i*i;
            });
        }
        returnarr;
    }
    //执行fn返回数组,数组中含有方法
   
var arr1 = fn();
    for(var j=0;j<arr1.length;j++){
        console.log(arr1[j]());
    }
</script>

结果如下:

期望的是返回

0

1
4
9
16

此时分析重点,引用,fn返回的是对数组arr的引用,arr里添加的方法中的返回值是对i的引用,在方法fn执行完成之后,其引用的i已变成了5,而在fn执行完成之后,再调用数组之中的方法,此时返回的自然都是i=5的平方.也就是5个25;

如果说将需求改成必须要返回我之前的期望结果会如何?此时就需要第二句话,函数每执行一次,会在内存中生成一个副本,包括其内的参数,值等

<script>
    function fn(){
        var arr = [];
        for(var i=0;i<5;i++){
            (function(n){
                //向数组中添加方法,返回i的平方
               
arr.push(function(){
                    return n*n;
                });
            })(i);

        }
        returnarr;
    }
    //执行fn返回数组,数组中含有方法
   
var arr1 = fn();
    for(var j=0;j<arr1.length;j++){
        console.log(arr1[j]());
    }
</script>

结果如下:

在for循环中不再是添加一个返回i引用的平方的函数,而是在for循环中每次执行一个自执行函数,形参为n,生成i的副本n,并在这个函数中向数组中添加返回副本n的平方的函数,由于JS中函数每执行一次都会拷贝一个副本,其中包括传入的形参,所以实时的形参也就被保存下来了.

四,隐藏在闭包下的内存问题

       每一次定义一个函数都会产生一个作用域链,而当js查找变量的时候,会优先当前作用域链的第一个对象进行查找,如果找不到则会前往下一个,直到找到或者报错.如果需要访问函数中定义的局部变量,只能在函数定义的环境中能访问到,如果需要在其他地方访问那么需要将定义环境延长,或者将本该在调用之后就销毁的局部变量,强制延长成全局变量,而在使用之前并不确定到底要使用谁,这就造成了所有的局部变量都变为了全局变量,从而增加了很大的内存开销.

此处的建议是:

       如果获取了函数的内部数据,最后一定要把数据的引用设为null!

将上述代码的arr1在for循环结束之后,添加arr1=null,此处将引用设为了null,那么垃圾回收会将其进行内存回收,从而优化内存问题.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值