闭包,JavaScript 中非常有用的一个特性,初接触它时你可能觉得它很神秘让人摸不着头脑,但是!!你不用望而却步!今天我将带你轻松get闭包原理,将它理解透彻。
在进入我们今天的主题闭包之前,我们先要确保自己知道与闭包息息相关的两个概念,那就是调用栈和作用域链。
调用栈
在 JavaScript 中,调用栈是用来管理函数调用关系的一种数据结构。当代码执行到一个函数时,会将该函数的信息(如参数、局部变量)压入调用栈中;当函数执行完成后,会将其从调用栈中弹出。这种先进后出的数据结构使得 JavaScript 可以追踪函数的调用关系,确保代码的执行顺序和上下文的切换。
作用域链
在 JavaScript 中,每个函数都有自己的作用域,并且可以访问其外部作用域的变量。当代码在一个作用域中无法找到某个变量时,JavaScript 引擎会沿着作用域链向上查找,直至全局作用域。这种链状的查找关系就是作用域链。
如果你对这两个概念还不太清楚的话,请先移步 《JavaScript调用栈和作用域链指南:新手也能快速上手》将其弄懂,这对我们接下来要理解的闭包非常重要!!
闭包
话不多说,我们进入正题,先看以下代码:
javascript
function foo(){
var myName = '阿美'
let test1 = 1
const test2 = 2
var innerBar = {
getName:function(){
console.log(test1);
return myName
},
setName:function(newName){
myName = newName
}
}
return innerBar;
}
var bar = foo()
bar.setName('洋洋')
console.log(bar.getName);
我们定义了一个函数foo,在函数foo里,声明了myName,test1,test2和innerBar;在对象innerBar里,我们又写了getName和setName两个函数体;getName里面打印test1,并return出myName的值,setName里传入一个newName,会把newName的值赋给myName;函数foo最终返回innerbar。最后我们把函数foo调用返回的值赋给bar。
foo调用后返回的是什么呢?,是对象innerBar,此时被赋给bar,即bar = innerBar。
bar.setName('洋洋')
执行后myName被重新赋值为洋洋
,最后执行console.log(bar.getName)
会有两个打印,第一个是getName里的输出test1,另外一个就是getName里return出的myName,结果是1和洋洋
。
把这段代码弄明白后,我们来看调用foo后的调用栈:
首先编译全局将全局执行上下文入栈,之后执行foo调用后将foo执行上下文入栈,再执行将各个变量的值赋给它们,最后return出innerBar。当整个foo调用执行完成后,foo执行上下文将会销毁出栈,不再占用调用栈的空间:
但是!!执行foo的调用并将返回值赋给bar之后,我们需要执行bar.setName('洋洋')
,此时setName的执行上下文入栈,那么setName的outer是指向哪的呢?setName函数处于foo的作用域里,所以setName的outer指向foo:
setName要将newName的值赋给myName,而myName在foo的变量环境里,好,到这里问题就出来了,foo执行完了要销毁它的执行上下文,但是执行setName又需要用到myName,而setName的myName要顺着outer的指向去foo执行上下文里找,那么foo里面的myName就不能被销毁掉,因为它之后还会被用到。
代码最后一行也是一样,console.log(bar.getName)
,getName函数需要用到foo里的test1,那么test1也是不能被销毁的。
所以,当销毁foo执行上下文的时候,就会检索出来之后还会被用到的test1和myName,它们将不会被销毁而是遗留下来成为一个闭包:
现在,我们就可以解释闭包了:
在js中根据词法作用域的规则,内部作用域总是能够访问外部函数中的变量的,当通过调用一个外部函数,返回一个内部函数后,即使外部函数已经执行完毕,但是内部函数引用了的外部函数中的变量依然会保存在内存中,我们把这些变量的集合叫做闭包。
以刚刚的例子来说就是,我们调用foo函数的时候,会返回它函数体里面的innerBar,而innerBar里面是包含函数getName和setName的,当foo调用执行完毕后,由于getName和setName还引用了foo里的变量,所以被引用的变量还会保存在内存中,这些被引用的变量的集合就叫做闭包。
今天的知识,你学到了吗ヾ(✿゚▽゚)ノ