当前位置: 首页 > 科技观察

闭包是如何产生的?你知道吗?

时间:2023-03-21 21:08:52 科技观察

大家好,我是前端西瓜哥。这次我们从内存管理的角度来看一下闭包是如何产生的。我们知道,当一个函数被调用时,实际上会产生一个临时的调用栈。这些调用栈保存了执行上下文,实际上保存在栈内存中。每次执行函数时,都会临时保存函数中的局部临时变量。如果此时函数调用了另一个函数,则另一个函数下的局部变量也会被保存。这样,我们就有了一个栈。当一个函数完成执行时,其对应的局部临时变量将被销毁。保存局部变量以保护上下文场景。例如:functiona(){consta_num=99;consta_obj={val:"a"};b();}functionb(){constb_str="文本";c();}functionc(){constc_bool=true;//调试器}a();这里我们嵌套调用函数a、b、c,会产生如下调用栈。基本类型的临时变量会直接存放在栈内存中。对于引用类型,它们会在堆内存中生成,然后获取地址并存储在栈内存中。为什么引用类型不直接放在栈内存中呢?因为栈内存不是很大,很容易溢出栈,而引用类型通常很大。调用闭包的生成函数后,在其中声明的临时变量将被销毁。理论上应该是这样,但是如果使用闭包,就可以让临时变量不被破坏。示例:函数createCounter(){让计数=0;letotherVal="其他值";returnfunctioncounter(){//调试器;控制台日志(计数++);};}constcounter=createCounter();console.log(counter());执行过程是:当函数createCounter被执行时,会创建一个空的context对象。当遇到内部函数计数器时,它会预先扫描内部函数计数器在createCounter下使用的方便,最后扫描出count变量。所以在堆内存中创建一个Closure(createCounter)对象,并向其添加计数。otherVal不会被添加到闭包对象中,因为它没有被使用。这个内部函数最终被返回并被引用,闭包永远不会被销毁。这个闭包对象可以使用DevTool观察:所以,如果一个闭包返回的函数在执行后没有被使用,它应该被设置为null。否则它关联的闭包对象将一直在那里占据内存。多个内部函数共享一个闭包对象此外,如果有多个内部函数,这些函数将共享同一个闭包对象。即使其中一个内部函数没有返回,它也会向闭包对象添加内容。下面我们添加了一个printOtherVal的内部函数,它不返回,但是仍然使得返回的counter函数对应的闭包对象携带了它不需要的otherVal变量。这是JS引擎的关闭策略的问题,理论上应该不会出现这么奇怪的效果。当函数最后被调用时,会产生一个调用栈,将当前函数上下文压入栈中,保存基本类型变量。引用变量在堆内存中创建,然后在栈内存中引用。因为函数在JavaScript中是一等公民,所以有闭包的概念。当找到内部函数时,会创建一个闭包对象,其中使用的外部函数变量会保存在闭包对象下。稍后,当调用内部函数时,变量将从闭包中提取,如果找不到则从全局上下文中提取。