深入理解闭包
先不说闭包是什么,我们先理解js作用域的概念,作用域,简单说就是定义一个变量,我们能在哪些范围内使用它,
var a = 1; function fn() { var b = 2; console.log(a); console.log(b); } fn(); // 1 2
上面是一个简单的例子,a是全局变量,b是函数fn作用域内的变量,js是链式作用域,当使用一个变量时,会沿着作用域向上寻找该变量。所以能在函数内部能打印出a、b的值。反之,
var a = 1; function fn() { var b = 2; } console.log(a); // 1 console.log(b); // 报错 b is not defined
在全局作用域无法访问fn作用域内的变量。
闭包是怎么一种存在呢,它打破了这种“函数外部无法访问函数内部变量”的约束,来看看它是如何做到的
function foo() { var name = "Eva"; function bar() { console.log(name); } return bar; } var result = foo(); result(); // Eva
在foo函数内定义了一个函数bar,并且在bar中console.log了foo中的变量name,并将bar返回,在全局执行result,打印出了name的值,也就是说我们上面无法实现的在全局作用域无法访问函数内部变量的问题是不是被解决了??没错!!!
为什么呢?因为通常一个函数执行完后,就会被垃圾回收机制回收,那它的作用域也咩有了,但是这个例子中在将bar返回的时候,foo函数作用域作为它的父作用域被保存下来了,并没有被回收,所以依然能访问到foo的作用域。这也就是它为什么会内存泄露的原因,如果不手动释放,它会一直存在着。
我们再看几个做题时经常碰到的题目,
for (var i = 0; i < 5; i++) { setTimeout( function timer() { console.log(i); }, 1000); } // 5 5 5 5 5
结果是5个5,因为所有的console语句的i变量都是从for循环这个作用域取的,而1秒之后,这个作用域的i已经是5了,所以会打印出5个5.
for (var i = 1; i <= 5; i++) { (function(i){ setTimeout( function () { console.log(i); }, 1000 ); })(i); } // 1 2 3 4 5
上面的这段代码,for循序里定义了一个立即执行函数,这种函数一般用于构造私有变量,避免全局污染,每个console语句都有自己的作用域,且作用域中保存了当前的i的值,所以在1000ms后执行这些console语句,都是从各自作用域获取的i。
我们再看一个例子,
for (let i = 1; i <= 5; i++) { setTimeout( function timer() { console.log(i); }, 1000 ); } // 1 2 3 4 5
这段代码使用了let来定义变量,我们都知道let定义的变量的块级作用域,将i值绑定到循环的每次迭代中,也就是每个console语句上,也很好的解决了问题。
总结:在《js高级程序设计》中,将闭包定义为有权访问另一个函数作用域中的变量的函数。但是我更倾向于另一种说法:引用了自由变量的函数。函数中定义一个函数来访问父函数变量并返回该函数只是闭包的实现方式。而返回的才是闭包,携带自由变量作用域的一个函数。
- 点赞
- 收藏
- 关注作者
评论(0)