浅谈对JavaScript中闭包的理解

前  言

在前端开发中闭包是一个很重要的知识点,是面试中一定会被问到的内容。


一、闭包的定义

函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起构成闭包(closure)。也就是说,闭包可以让你从内部函数访问外部函数作用域。在 JavaScript 中,每当函数被创建,就会在函数生成时生成闭包。

简单来说,闭包 就是一个定义在函数中的函数

例如:

function Clos(){
    
    
	var age = 32;
    function getNum(){
    
    
    	console.log(age);//32
        return age;
    }
    return getNum;
}
var get_A = Clos();//get_A将会得到Clos()最后的运行返回的结果.即getNum()函数体
get_A();//拿到了变量age的值:32
console.log(age);//报错:ReferenceError: age is not defined

上面这段代码中的getNum()函数就是一个闭包,它定义在名为Clos()函数体中。


二、闭包的作用

函数内部定义的变量属于局部变量,局部变量的生命周期是:当它所在的函数被调用的时候,就是开始,当调用执行一旦结束,局部变量就会被释放,当我们需要函数内部变量时,他已经被释放了,读取不到了,这个时候怎么解决?我们就要想办法延长他的生命周期。

闭包的目的也可以说就是这个,延长局部变量的生命周期,当函数执行完毕以后,局部变量不可以被内存释放,然后让外部可以访问到这个变量。

我们还是先回到上面的代码进行分析:
代码在运行到

console.log(age);

这句话本意是我们本想调用变量age进行输出。但是编译结果报错:age未定义。
这是因为变量age定义在Clos()函数中,作用域也就局限于Clos()函数中。变量age相当于Clos()函数私有属性(private value)。

那么我们想要调用age就没有办法了吗?
这时,闭包的作用就体现出来了。Clos()函数的返回值指向闭包函数getNum()。因为闭包函数getNum()定义在Clos()函数中,所以我们可以用它来获取变量age的值。闭包就相当于公有方法(Public Method) 用于给外界提供接口来访问内部数据。


三、闭包的利与弊

先来说说弊端

也正是由于闭包机制,使得JavaScript的垃圾回收机制不会收回Clos()函数所占的资源,因为闭包执行依赖的变量在它里面。如果代码中闭包过多,会对内存造成溢出,对页面的加载造成影响,所以对于闭包的使用需要谨慎。

再来看看好处

JavaScript在多人协作开发项目时,如果定义过多的全局变量,有可能造成全局变量命名冲突,使用闭包可以很好的解决。

    //公司程序员小李和小王一起协作开发项目
    var name = '小李';//小李定义了一个全局变量
    //var name = '小王'; //如果小王继续定义相同名字变量就会覆盖小李写的变量name的值
    
    var private = (function(){
    
    
        var name ='小王';
        function getName() {
    
    
            return name;
        }
        return getName;
    }());

    console.log("小王定义的name:"+private());
    console.log("小李定义的name:"+name);

上面代码中,第一个var name='小李'定义在window全局作用域中。第二个var name = '小王'定义在函数体中,作用域互不影响,这就避免了全局污染。


总结

闭包主要有以下几个特点:

  1. 函数套函数,闭包一定有嵌套函数。闭包的目的就是访问函数内部的局部变量,如果不定义局部变量,那就达不到我们的目的——延长变量生命周期。

  2. 外层函数一定有局部变量,且内层函数一定操作了外层函数的这个变量.

  3. 外层函数一定把内层函数返回外部,使用return。

在做闭包的问题时,我们还需要注意以下两点:

  1. 外层函数被多次调用,都会创建新的作用域,也就是说内层函数操作的外层函数的局部变量之间是不会影响的
  2. 外层函数返回的内层函数被调用几次,内层函数操作的外层函数的局部变量就会变化几次
//模拟了用户去银行存钱并查询余额的功能
function Person(name){
    
    
    var money = 0;
    function setMoney(m){
    
    
         money = m;
    }
    function getMoney(){
    
    
        return money;
    }
    return {
    
    
        name:name,
        setMoney:setMoney,
        getMoney:getMoney
    }
}

//第一次调用
var LiLei = person('李磊');
LiLei.setMoney(4500);//用户李磊存入4500元


//第二次调用
var WangWu = person('王武');
WangWu.setMoney(7000);//用户王武存入7000元

console.log(LiLei.getMoney());//得到并输出账户余额:4500
console.log(WangWu.getMoney());//得到并输出账户余额:7000
var LiLei = person('李磊');
var WangWu = person('王武');

这句代码模拟了两个不同用户开户的过程,他们共同调用一个函数,操作变量money值的修改。正因为每次重复调用会开辟新的作用域,所以李磊和王武的钱不会存到一起去。李磊和王武的钱分别保存在两个不同的内存空间中。

猜你喜欢

转载自blog.csdn.net/qq_35370002/article/details/107885880