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

引言

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

思考题

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

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

结尾

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

相关推荐
也无晴也无风雨1 小时前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
Martin -Tang2 小时前
Vue 3 中,ref 和 reactive的区别
前端·javascript·vue.js
SRY122404193 小时前
javaSE面试题
java·开发语言·面试
FakeOccupational3 小时前
nodejs 020: React语法规则 props和state
前端·javascript·react.js
放逐者-保持本心,方可放逐4 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架
曹天骄5 小时前
next中服务端组件共享接口数据
前端·javascript·react.js
阮少年、5 小时前
java后台生成模拟聊天截图并返回给前端
java·开发语言·前端
不二人生6 小时前
SQL面试题——连续出现次数
hive·sql·面试
郝晨妤6 小时前
鸿蒙ArkTS和TS有什么区别?
前端·javascript·typescript·鸿蒙
AvatarGiser7 小时前
《ElementPlus 与 ElementUI 差异集合》Icon 图标 More 差异说明
前端·vue.js·elementui