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

引言

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

思考题

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

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

结尾

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

相关推荐
代码老中医9 分钟前
接手老项目的一个月,我重构了那个2000行的“祖传”React组件
前端
飘逸飘逸20 分钟前
Autojs进阶前言
android·javascript
叫我一声阿雷吧26 分钟前
JS 入门通关手册(01)|零基础入门:JavaScript 到底是什么?学完能干嘛?
javascript·前端入门·js入门
用户830407130570139 分钟前
路由传参刷新丢失问题:三种解决方案与最佳实践
前端
从文处安41 分钟前
「前端何去何从」高效提示词(prompts):前端开发者的AI协作指南
前端·aigc
大时光43 分钟前
gsap--《pink老师vivo官网实现》
前端
www_stdio1 小时前
全栈项目第五天:构建现代企业级 React 应用:从工程化到移动端实战的全链路指南
前端·react.js·typescript
my_styles1 小时前
window系统安装/配置Nginx
服务器·前端·spring boot·nginx
茶杯梦轩1 小时前
从零起步学习并发编程 || 第八章:线程池实战(避坑指南与最佳实践)
服务器·后端·面试
神奇的程序员1 小时前
不止高刷:明基 RD280UG 在编码场景下的表现如何
前端