感觉js闭包可以引发一系列面试中比较常考的问题,打算单独写一篇文章加以总结
-
如果之前对作用域的相关知识点了解的话,闭包的概念也就非常好理解了。
闭包是指一个可以访问另外一个函数作用域中的变量的函数(定义在函数内部的函数)。
作用大概就是形成块级作用域以及私有变量。这是我觉得比较通俗易懂的解释。当然es6中的let声明变量可以完美解决块级作用域的问题。
-
说到闭包就不得不说到作用域,在JS中,函数的可以允许嵌套的。即,在一个函数的内部声明另一个函数。以这篇博客的代码为例:
function A(){ var a=1; function B(){ //在A函数内部,声明了函数B,这就是所谓的函数嵌套。 var b=2; } }
对于A来说,A函数在执行的时候,会创建其A函数的作用域, 那么函数B在创建的时候,会引用A的作用域,类似下面这样
A的作用域
函数B在执行的时候,其作用域类似于下面这样:
B的作用域
从上面的两幅图中可以看出,函数B在执行的时候,是会引用函数A的作用域的。所以,像这种函数作用域的嵌套就组成了所谓的函数作用域链。当在自身作用域内找不到该变量的时候,会沿着作用域链逐步向上查找,若在全局作用域内部仍找不到该变量,则会抛出异常。
-
闭包所引起的问题,看下面的这道例子:
var funB, funC; (function() { var a = 1; funB = function () { a = a + 1; console.log(a); } funC = function () { a = a + 1; console.log(a); } }()); funB(); //2
我们可以看到,变量a被“污染”了,意思就是说,对于 funB和funC两个闭包函数,无论是哪个函数在运行的时候,都会改变匿名函数中变量a的值,这种情况就会污染了a变量。
两个函数的在运行的时候作用域如下图:
闭包引起的变量污染问题
这这幅图中,因为funB和funC与变量a在同一作用域,所以变量a可以被函数funB和funC改变,就相当于外部作用域链上的变量对内部作用域来说都是静态的变量。
所以,为了解决这种变量的污染问题---而引入的闭包的另外一种使用方式。
思想就是:既然外部作用域链上的变量时静态的,那么将外部作用域链上的变量拷贝到内部作用域不就可以啦!! 具体怎么拷贝,当然是通过函数传参的形式啊。
这里就再次用到立即执行函数了,修改后的代码如下:
var funB,funC;
(function () {
var a = 1;
(function (a) {
funB = function () {
a = a + 1;
console.log(a);
}
}(a));
(function (a) {
funC = function () {
a = a + 1;
console.log(a);
}
}(a));
}());
funB()||funC(); //输出结果全是2 另外也没有改变作用域链上a的值。
在函数执行时,内存的结构如图所示:
![](https://img.haomeiwen.com/i7517820/ba7f7abe0ccd30ff.png)
由图中内存结构示意图可见,为了解决闭包的这种变量污染的问题,而加了一层函数嵌套(通过匿名函数自执行)
类似的问题还可以参考这篇博客:用立即执行函数 解决闭包问题
网友评论