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

引言

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

思考题

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

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)了

结尾

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

相关推荐
Tajang几秒前
推荐一个浏览器代理插件(Tajang Proxy),支持Chrome和Edge
前端·chrome·网络安全·渗透测试·靶场·edge
泽虞2 分钟前
《Qt应用开发》笔记p3
linux·开发语言·数据库·c++·笔记·qt·面试
鹏多多4 分钟前
前端音频兼容解决:音频神器howler.js从基础到进阶完整使用指南
前端·javascript·音视频开发
龙仔CLL36 分钟前
使用vue-pdf做本地预览pdf文件,通过垂直滚动条展示全部pdf内容,不展示分页按钮
前端·vue.js·pdf
前端架构师-老李36 分钟前
12、electron专题(electron-builder)
前端·javascript·electron
IT_陈寒1 小时前
JavaScript性能飞跃:5个V8引擎优化技巧让你的代码提速300%
前端·人工智能·后端
艾小码1 小时前
这份超全JavaScript函数指南让你从小白变大神
前端·javascript
骑士雄师1 小时前
Java 泛型中级面试题及答案
java·开发语言·面试
reembarkation1 小时前
vue 右键菜单的实现
前端·javascript·vue.js
00后程序员张3 小时前
Fiddler抓包工具使用教程,代理设置与调试方法实战解析(含配置技巧)
前端·测试工具·ios·小程序·fiddler·uni-app·webview