闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。
我们先来看一个闭包的例子
function father() {
var name = "Burc"; // name 是一个被 father() 创建的局部变量
function child() { // child() 是内部函数,一个闭包
alert(name); // 使用了父函数中声明的变量
}
return child;
}
var myFunc = father();
myFunc();
在这个例子中,在内部函数 child() 在执行前,就已经被外部函数 father() 返回。
也许第一感觉这段代码不能运行。因为在一些编程语言中,函数中的局部变量仅在函数的执行期间可用。一旦 father() 执行完毕,我们会认为 name 变量将不能被访问。然而,因为代码运行得没问题,所以很显然在 JavaScript 中并不是这样的。
这个谜题的答案是,JavaScript中的函数会形成闭包。 闭包是由函数以及创建该函数的词法环境组合而成。这个环境包含了这个闭包创建时所能访问的所有局部变量。在我们的例子中,myFunc 是执行 father 时创建的 child 函数实例的引用,而 child 实例仍可访问其词法作用域中的变量,即可以访问到 name 变量 。
闭包的最大特性就是,如果里函数引用(or访问,这俩词在这个语境下是等价的)了外函数的某个变量,那这个变量就能享受和全局变量一样的特权,不会被回收!除非你关闭页面or浏览器!
关于内存销毁强烈推荐这篇文章 原文链接 ,简单来说就是,没有用的局部变量就会被销毁内存,而在闭包中,内部函数引用了外部函数的变量,所以外部函数的局部变量即 name 变量就可以具有全局变量的某种特权,不会被回收,不会被销毁。
如果不是很理解,我们看一下下面这两个例子
//例一
btn.onclick = function (){
var num = 1;
num++;
console.log(num);
}
//例二
(function (){
var num = 1;
btn.onclick = function (){
num++;
console.log(num);
}
})();
对于例一来说,每次控制台输出都是1,因为在函数执行完毕变量num就被销毁,但是对于例二来说,控制台依次输出1,2,3...... 因为内部函数引用了外部函数的变量,所以外部函数的局部变量没有被销毁。
在循环中创建闭包,一个经典的问题
<body>
<!-- 在页面添加三个按钮 -->
<input type="button" value="1">
<input type="button" value="2">
<input type="button" value="3">
<script>
// 获取页面所有的input
var aBtn = document.getElementsByTagName("input");
// 循环绑定点击事件,当然毫无疑问这里点击之后会弹出3,
//因为我们知道js是单线程的,当基本逻辑语句执行完之后,才会执行点击事件
//而当你触发点击事件的时候,for循环都已经跑完了,所以i已经变成了3;
// for(var i = 0; i < aBtn.length; i++){
// aBtn[i].onclick = function(){
// console.log(i);
// }
// }
// 解决方法1:也是最简单的方法,就是将for循环的var变成let
//这样当点击每个按钮的时候,就会依次弹出0,1,2;
//let是ES6新增的一个变量声明方式,拥有块级作用域;
for(let i = 0; i < aBtn.length; i++){
aBtn[i].onclick = function(){
console.log(i);
}
}
//解决方法2:利用闭包(说是闭包,貌似也不完全是),也就是函数作用域访问原则的特性
//函数内部可以访问外部的变量,外部却访问不了里边的;
//个人感觉主要是在匿名函数里面的i变量没有被销毁内存
// for(var i = 0; i < aBtn.length; i++){
// (function(i){
// aBtn[i].onclick = function(){
// console.log(i);
// }
// })(i);
// }
</script>
</body>
参考链接