【前端 教程】闭包 详解
1、闭包概念
闭包就是指有权访问另一个函数作用域中的变量的函数。或简单理解为定义在一个函数内部的函数,内部函数持有外部函数内变量的引用。
js的作用域分两种,全局和局部,基于我们所熟悉的作用域链相关知识,我们知道在js作用域环境中访问变量的权利是由内向外的,内部作用域可以获得当前作用域下的变量并且可以获得当前包含当前作用域的外层作用域下的变量(也叫“JS的链式作用域”),反之则不能,也就是说在外层作用域下无法获取内层作用域下的变量,同样在不同的函数作用域中也是不能相互访问彼此变量的,那么我们想在一个函数内部也有限权访问另一个函数内部的变量该怎么办呢?闭包就是用来解决这一需求的,闭包的本质就是在一个函数内部创建另一个函数。
【JS链式作用域】子对象会一级一级向上寻找所有父对象的变量,反之不行。
【JS变量的两种作用域】全局变量、局部变量(函数内):js中函数内部可以读取全局变量,函数外部不能读取函数内部的局部变量。
需要先了解 JavaScript 语言特有的一种结构–链式作用域,即为,子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象是可见的,反之则不成立。
了解这点后,可做以下推论,外部无法访问父函数的局部变量,而子函数可以访问父函数的局部变量,只要将子函数作为返回值,就可以在外部间接访问到父函数的局部变量了。
可能看这么些定义会感到一头雾水
表急
function A() {
var naem = "奋斗中的编程菜鸟";
return function() {
return name;
}
}
var B = A();
console.log(B()) //奋斗中的编程菜鸟
在这段代码中,A()中的返回值是一个匿名函数,这个函数在A()作用域内部,所以它可以获取A()作用域下变量name的值,将这个值作为返回值赋给全局作用域下的变量B,实现了在全局变量下获取到局部变量中的变量的值。
因此可以把闭包简单理解成"定义在一个函数内部的函数"。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
2、闭包的几个经典例子
No.1
function Fn() {
var num = 99;
return function() {
var n = 0;
console.log(++n);
console.log(++num);
}
}
var Fn1 = Fn();
Fn1() //1 100
Fn1() //1 101
一般情况下,在函数fn执行完后,就应该连同它里面的变量一同被销毁,但是在这个例子中,匿名函数作为Fn的返回值被赋值给了Fn1,这时候相当于Fn1=function(){var n = 0 … },并且匿名函数内部引用着Fn里的变量num,所以变量num无法被销毁,而变量n是每次被调用时新创建的,所以每次Fn1执行完后它就把属于自己的变量连同自己一起销毁,于是乎最后就剩下孤零零的num,于是这里就产生了内存消耗的问题。
No.2
再来看一个经典例子-定时器与闭包:写一个for循环,让它按顺序打印出当前循环次数。
for (var i=0; i<5; i++) {
setTimeout(function() {
console.log(i);
}, 100);
}
输出结果如下:
按照预期它应该依次输出1 2 3 4 5,而结果它输出了五次5,这是为什么呢?原来由于js是单线程的,所以在执行for循环的时候定时器setTimeout被安排到任务队列中排队等待执行,而在等待过程中for循环就已经在执行,等到setTimeout可以执行的时候,for循环已经结束,i的值也已经到5了,所以打印出来五个5,那么我们为了实现预期结果应该怎么改这段代码呢?
答案:使用闭包!!!
for (var i=0; i<5; i