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

引言

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

思考题

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

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

结尾

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

相关推荐
DogDaoDao4 分钟前
leetcode 面试经典 150 题:长度最小的子数组
算法·leetcode·面试·双指针·滑动窗口·数据结构与算法·子数组
长安051118 分钟前
面试经典题目:LeetCode55_跳跃游戏
游戏·面试·职场和发展
console.log('只想发财')32 分钟前
前端生成指定模板的word文件
前端·word
憨憨小江1 小时前
Vue todoList小项目记录
前端·javascript·vue.js
蕉君桑1 小时前
前端优雅(装逼)写法(updating····)
开发语言·前端·javascript
测试老哥1 小时前
面试中常问的软件测试面试题
自动化测试·软件测试·python·功能测试·测试工具·面试·职场和发展
前端与小赵1 小时前
有哪些 Web 应用程序类型
前端
红绿鲤鱼1 小时前
Next.js v15 - 服务器操作以及调用原理
前端·next.js
Cchengzu1 小时前
百度2021校招Web前端研发工程师笔试卷(第一批)
前端