JS通俗易懂的理解闭包
本文章举了3个生动形象的故事来比喻闭包,即使你是
JS
新手,你也可以弄清楚闭包!
初步了解概念
首先,这是一个函数对吧?
var fun = function(){
}
现在,我们往里面添加几个变量
var fun = function(){
var a = 1
var b = 2
var c = 3
}
然后,我们执行这个函数
var fun = function(){
var a = 1
var b = 2
var c = 3
}
fun()
好的,现在我要提出一个问题了:当
fun()
运行之后,里面的a、b、c变量还存在吗?答案是否定的,在函数执行完毕后,里面这些变量就会被销毁掉,因为这些变量没有任何必要存在了,对吧,所以他们理所应当被内存管理机制给销毁了。
好,明白了“垃圾销毁机制”后,我们继续,我们往刚才的函数中再放入一个子函数,我们将这个子函数称之为“内部函数”,而刚才的函数我们便可以称之为“外部函数”:
var fun = function(){
var a = 1
var b = 2
var c = 3
var d = function(){
}
}
fun()
一切很正常,对吧,那么现在我们在这个“内部函数”里面引用一下
c
这个变量:
var fun = function(){
var a = 1
var b = 2
var c = 3
var d = function(){
console.log(c)
}
}
fun()
一切仍然很正常,直到这一步,我们将这个“内部函数”,通过
return
返回出去,并且用一个变量来引用保存这个返回出去的“内部函数”:
var fun = function(){
var a = 1
var b = 2
var c = 3
var d = function(){
console.log(c)
}
return d
}
var s = fun()
而现在,闭包就真正的诞生了!前面我们说过,函数在执行完毕之后就会将里面所有变量都销毁,但是这是有例外的,被
return
出去的“内部函数”被引用了,因为外面引用了return
出去的变量,你可以看到我们用一个名为s
的变量保存了“内部函数”这个返回,这样保证了“内部函数”被引用,从而使其不会被“垃圾销毁机制”所销毁,因为它被引用了,所以它有作用,所以它不是垃圾,所有它不会被销毁,而最关键的是这个“内部函数”里面还引用了c
这个变量,所以c
也跟着变成了有作用的变量,也不会被销毁,所以,闭包=这个“内部函数”(或者也可以理解为闭包=这个“内部函数”的内在环境),所以,闭包的作用是使得变量不被销毁,且闭包环境是一个封闭的作用域。
通俗易懂的例子
无论能不能理解上面的概念,没关系,下面有
3
个通俗易懂的例子,全部看完,你肯定可以理解!
例子1
我们把外部函数想象成一座垃圾处理厂,里面有很多垃圾,而垃圾就是变量,原本垃圾处理厂会将这些垃圾销毁,而我,也就是内部函数,我不顾危险冲进垃圾堆里挑选了一个垃圾,然后拿着走出了垃圾处理厂,现在,只要我不出意外,这个垃圾就会一直跟随着我,这个垃圾也不会被销毁,除非我死了,垃圾掉落在了地上,那么最终才会被回收销毁。
概念 | 对应的比喻 |
---|---|
外部函数 | 垃圾处理厂 |
内部函数/闭包 | 我 |
被引用的变量 | 被选中的垃圾 |
内部函数去到外部的方式 | 走出去 |
例子2
假设外部函数是一个死刑监狱,在死刑监狱中,所有犯人都是死刑,而我也是其中一员,并且我与其中一个犯人是生死之交,即使他是残疾人,我也必须救他,于是我经过不断努力终于救出了他,并且通过挖地道逃出了监狱,但是由于他是残疾人,如果我被抓了,他也会立即被抓,毕竟现在所有警察都在找我们,而一旦被抓的后果就是死刑!所以我与他必须小心翼翼的苟活着,我不能死!我想,如果我死了,他也就死了。
概念 | 对应的比喻 |
---|---|
外部函数 | 死刑监狱 |
内部函数/闭包 | 我 |
被引用的变量 | 残疾人兄弟 |
内部函数去到外部的方式 | 挖地道逃出 |
例子3
假设外部函数是一座满是丧尸的城市,在城市中,有许多幸存者,这些幸存者就是变量,而我是其中一员,只不过,我是一个婴儿!我的父母不知去向,我一个人在家里,岌岌可危,但就在这时,内部函数出现了,他是一名英雄,他准备带着我逃出这座城市,但糟糕的是,这座城市四面环海,没有任何陆地通往外面!可恶!我们只能无助的在海边不断寻找,天啊,我们居然找到了一支小船,这支小船就是return,太好了,我们乘上这支小船,然后在海上漂流,幸运的是,我们最终到达了一个荒岛,在这个荒岛上,我们最终幸存了下来,并且没有任何的丧尸和威胁,我们安全了!但是由于我只是一个婴儿,所以英雄不能死,一旦英雄死了,我作为一个婴儿必须也存活不下去。
概念 | 对应的比喻 |
---|---|
外部函数 | 满是丧尸的城市 |
内部函数/闭包 | 英雄 |
被引用的变量 | 我(婴儿) |
内部函数去到外部的方式 | 坐船 |
标准的理解
最后,我们有必要看下标准的解释和代码示例,以便更准确的理解闭包:
- 闭包是什么?
闭包是指内部函数捕获并持有外部函数作用域中的变量,使得这些变量在外部函数执行完毕后仍然可以被内部函数访问。
- 闭包的作用是什么?
闭包的作用是:使得原本会被销毁的变量可以被内部函数所引用而不被销毁。
- 闭包的简单示例?
第一个例子:
var fun = function(){
var a = 1
var b = 2
var c = 3
var d = function(){
console.log(c)
}
return d
}
var s = fun()
// s就是闭包
第二个例子:
var btns = document.querySelectorAll(".btn")
for (var i = 0; i < btns.length; i++) {
(function (index){
btns[index].addEventListener('click',function (e){
console.log(index)
})
})(i)
}
/*
在这个例子中:
1. “外部函数”是立即调用函数;
2. “内部函数”是事件的回调函数;
3. “被引用的变量”是index;
4. 没有return,但是“内部函数”作为事件监听器的回调函数,也就是被引用了,所以相当于return出去了。
这个例子是非常常见的用于解决for遍历和事件绑定结合时索引(index)总是相同的问题,因为如果不使用闭包解决的话,index的作用域永远都是在同一个,这将导致每个回调函数拿到的index都是同一个,所以在这个例子中使用闭包来分割作用域,使得每一个index都在单独的闭包中,也就是独立且封闭的作用域中。
*/