JS中的闭包,怎么用闭包

闭包:闭包是由函数以及创建该函数的词法环境组合而成,这个环境包含了这个闭包创建时所能访问的所有局部变量

闭包三要素:

  • 嵌套结构的函数

  • 内部函数访问了外部函数的变量

  • 在外部函数的外面,调用内部函数

/*闭包的常见写法*/
function fun(){
    var name = "haha";
    function innerFun(){
        console.log(name);
    }
    return innerFun;
}
fun()();
/*显然当执行fun()();语句的时候,控制台会打印"haha"出来*/

 

我们再来看看另外有段代码,这段代码跟上面的代码很相似,但这段代码并没有突出闭包的特性。

function fun(){
    var name = "hahahah";
    function innerFun(){
        console.log(name);
    }
    innerFun();
}
fun();
/*显然当执行fun()的时候,控制台会打印"hahahah"出来*/
/*虽然能打印结果,但这段代码中其实是在fun函数执行的时候innerFun函数在fun函数内部执行的*/

因为innerFun函数是在fun函数里面执行的,所以innerFun函数所引用的fun函数里面的name变量其实是在fun作用域里面的,所以这段代码并没有突出闭包的特点。

在分析代码段之前,我们先回顾两点知识:

  1. 从之前的学到的课程中我们了解到js作用域分两种,全局局部,在js作用域环境中访问变量的权利是由内向外的,内部作用域可以获得当前作用域下的变量并且可以获得当前包含当前作用域的外层作用域下的变量,反之则不能,也就是说在外层作用域下无法获取内层作用域下的变量,同样在不同的函数作用域中也是不能相互访问彼此变量的。简言之,小范围的作用域可以访问到大范围的作用域,但大范围的作用域不能够访问小范围的作用域

  2. 我们所经常用到的return可能没太注意它所返回的任何类型的数据是返回到哪里的, 在js中其实return 所返回的数据是returnwindow对象下面

这样我们可以重新分析第一段代码:

  1. 我们执行fun函数 --> fun()();

  2. fun函数里面,先定义了name变量,所以name的作用域就在fun函数里面

  3. 然后定义了一个innerFun函数,在innerFun函数里面运用到了fun函数里的name变量,这看似没有问题,因为小范围能访问大范围的变量。

  4. 然后fun函数把innerFun函数return出来了。

  5. 当我们先调用fun()时,fun函数已经把innerFun函数return到了window对象下面来了,这时候再执行fun()(),这样理论上window是大范围的作用域,是不能访问到fun小范围的作用域中的name变量的。

那么既然理论上大范围不能访问小范围,那为什么结果还是能打印出"haha"的结果呢?这就是闭包的,应该说的js机制的一大特点。js中的函数会形成闭包。 闭包是由函数以及创建该函数的词法环境组合而成。这个环境包含了这个闭包创建时所能访问的所有局部变量

所以闭包的特性:

  • 函数内部可以引用外部的参数变量

  • 起到了内部变量能长存的作用,不会快速被垃圾回收机制回收

  • 简易类特性

简易类特性:根据课程所讲的,我所理解到的就是 可以把闭包函数看成是类,类就相当于对象制造工厂,而闭包则是闭包制造闭包对象工厂,执行闭包函数,就会产生一个独立的闭包对象,每次执行闭包函数,它所产生的闭包对象都不同,这就时简易类特性。

function fun(){
    var num = 1;
    function innerFun(){
        console.log(num++);
    }
    return innerFun;
}
var f1 = fun();
var f2 = fun();
f1();
f2();
/*显然执行了f1();f2();后的结果是控制台输出两个1,
这两个1分别代表着不同闭包对象产生的num的值,
如果f1,f2是同一对象的话那执行f1()f2()就相当于执行了同一函数两次,
那结果输出应该是1,2而非两个1,所以闭包产生了两个不同的闭包对象*/

使用闭包常见的错误

<body>
    <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
    </ul>
    <script>
        function fun(){
            var li = document.getElementsByTagName('li');
            for(var i=0;i<li.length;i++){
                li[i].onclick = function(){
                        console.log("li:"+i);
                }
            }
        }
        fun();
    </script>
</body>

从结果看到,其实我们是想要点击li的时候输出对应的i值,而不是点击每个li都是输出同一个结果。那解决的办法可以是这样:

  • 使用多个闭包

function fun(){
    var li = document.getElementsByTagName('li');
    for(var i=0;i<li.length;i++){
        li[i].onclick = f(i);
    }
}
function f(i){
    return function(){
        console.log("li:"+i);
    }
}
fun();

这样输出的结果就是我们想要的效果了,这就是因为使用闭包函数使在内部的f(i)函数使用外部fun函数的i变量,i独立传进f闭包函数里面,使每个传进去的i的值都不一样。

  • 使用立即执行函数

function fun(){
    var li = document.getElementsByTagName('li');
    for(var i=0;i<li.length;i++){
        (function(){
            var id = i;
            li[id].onclick = function(){
                console.log("li   :"+id);
            };
        })()
    }
}
fun();

闭包一定要有返回值吗?

闭包不一定要有返回值,只要在闭包函数里面定义了一个函数再执行完闭包函数,在内存中就存在了闭包函数内部定义的函数,然后我们可以在闭包函数外面调用其内部函数了

var innerFun;
function fun(){
    var num = "xixix";
    innerFun = function (){
        console.log(num);
    }
}
fun();
innerFun();

不过这种写法得把函数名先定义在全局作用域里面,然后才能调用闭包函数fun内部写的函数innerFun,否则控制台会报错显示innerFun未定义。

性能问题

如果不是某些特定任务需要使用闭包,在其它函数中创建函数是不明智的,因为闭包在处理速度内存消耗方面对脚本性能具有负面影响。所以不是特殊的情况我们可以减少使用或避免使用会消耗内存和处理速度的闭包。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值