深入探讨:闭包的神秘面纱

引言

闭包,这个神秘的概念,在编程世界中扮演着重要的角色。它既是一种技术,也是一种思维方式。闭包的魅力在于它能够捕获上下文并延长其存在,使得代码能够更加灵活和强大。无论是在函数式编程还是异步编程中,闭包都展现出了其独特的价值。这篇文章将带领大家揭开闭包的神秘面纱。

思考题

掘友们先来思考这段代码最后会输出什么

js 复制代码
var arr = []
 for(var i = 0;i<10;i++){
     arr[i] = function(){
         console.log(i);
     }
 }
 for(var j = 0;j<arr.length;j++){
     arr[j]()//10 10 10 10 10 10 10 10 10 10 
 }

这还不简单?那不是输出(0-9)嘛?事实真的是这样吗?我们可以看到,在for循环中定义了一个全局变量i,在运行完第一个for循环以后,此时 i=10,所以当运行第二个for循环时,会打印10个10。

那我们要怎么样才能打印出(0-9)呢?

1.使用let

js 复制代码
var arr = []
 for(let i = 0;i<10;i++){
     arr[i] = function(){
         console.log(i);
     }
 }
 for(var j = 0;j<arr.length;j++){
     arr[j]()//0 1 2 3 4 5 6 7 8 9 
 }

let 让for循环变成块级作用域,于是就能实现输出(0-9)了

2.闭包

使用闭包也能达到同样的效果,等掘友们看完这篇文章,我相信你一定会用闭包来实现这个效果

闭包

我们先来看看这段代码:

js 复制代码
function foo(){
    var myName = '旭旭'
    let test1 = 1
    let test2 = 2
    var innerBar = {
        getName: function(){
            console.log(test1);
            return myName
        },
        setName: function(newName){
            myName = newName
        }
    }
    return innerBar
}

var bar = foo()
bar.setName('77')
console.log(bar.getName());//1 77

这段代码的结果为1 77.我们来分析一下,首先在调用栈中形成一个全局执行上下文,在变量环境中存入foo,bar,outer,然后开始调用foo(),返回了一个innerBar对象给bar,在调用完foo()方法以后,foo执行上下文销毁,再调用bar.setName(),打印输出。

掘友们问:为什么我的foo执行上下文已经销毁了,而我的bar还能调用setName方法并且可以输出test1呢?原来因为这里实现了闭包,当foo销毁时,V8发现foo函数内有一个内部函数调用了foo中的属性,于是不确定test1和getName,setName是否可以销毁,于是会将他们保留下来,这样我们仍然能够调用test1和方法。

整理一下闭包的概念:

在js中根据词法作用域中,内部函数总是可以访问其外部函数中声明的变量,当内部函数被返回到外部函数之外时,即使外部函数执行结束了,但是内部函数引用了外部函数的变量,那么这些变量依旧会保存在内存中,我们把这些变量的集合称为闭包

我们再来聊聊闭包的优缺点:

1.优点

闭包允许函数捕获并访问其声明范围外的变量,从而形成封闭的作用域。这种封装性使得我们能够隐藏特定变量,避免其被外部直接访问和修改,有利于代码的安全性和可维护性。它提供了一种灵活、安全、具有状态保持能力的编程方式,能够让我们更好地处理复杂的逻辑和需求。通过合理地运用闭包,我们能够写出更加模块化、可复用和可扩展的代码。

2.缺点

我们可以发现,在foo调用完毕后仍然可以访问foo内的属性和方法,这极易造成内存泄漏,并且闭包可能会占用更多的内存,因为它需要保留对外部环境的引用。如果闭包持有大量数据或者长期存在,可能会导致内存占用较高,需要特别注意内存管理。

解决思考题

学习完闭包以后,我们可以用这种方法来实现(0-9)的输出

js 复制代码
for(var i = 0;i<10;i++) {
//自执行函数
    (function a(j) {
       arr[i] = function() {
        console.log(j);
       }
    })(i)
}

在内部函数中调用了外部传入的i变量,在调用结束以后,不确定是否引用完毕,于是储存了十个i的值,再输出就可以输出(0-9)了

结尾

掘友们如此聪明,小小闭包想必也是难不倒大家,但在使用闭包时,还是要注意内存的消耗!

相关推荐
崔庆才丨静觅13 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606114 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了14 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅14 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅15 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅15 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment15 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅15 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊15 小时前
jwt介绍
前端