简单理解 JavaScript 闭包问题

从我16年开始接触前端,知道闭包这个词,已通过去两年了。这两年里,闭包这个概念我在不少地方了解过,却实在没有真的理解,长此以往,变成了一块心病。这不,趁着如今项目告一段落的时间,我又开始折腾它了,在网易云课堂上找了些资源来看。哈哈,多是由于已经作了些项目的缘故,此次终于能理解它究竟是个什么玩意儿了。javascript

概念

闭包,在《javascripts高级程序设计》里面是这样介绍的:闭包是指有权访问另外一个做用域中的变量的函数。额。。这句话我之前看过不少遍,但依然不是很懂,只知道它是跟做用域有关。如今我知道了,若是这句话换成:但凡是内部的函数被保存到了外部,一定生成闭包。这样就容易理解多了不是。
咱们如下面的这个代码块为例:前端

例子解释

function a() {
    const num = 100;
    function b () {
        num++;
        console.log(num);
    }
     return b;
}
const demo = a();
demo();
demo();

咱们先执行上述代码,看看结果是什么:java

为何是这样呢?数组

a()执行的结果是返回b,因此demo指的是b,则demo()指的是b();也就是说原先在a里面的b,在b的外部执行;
咱们已经知道了做用域和做用域链的概念,上面代码的做用域是这样的:缓存

a执行完,返回了b,此时的b只是声明,但还没调用,因此没有造成本身的AO,但做用域链和 a doing 时是同样的,因此虽然 a() 的做用域被销毁了,可是相同的一份却被b保存到了外面。这也就是内部函数被保存到了外面造成闭包的本质。这样也不难理解为何上述代码打印出来的值是那样的了:闭包

  • 执行第一个demo()时,也便是执行 b(),因为b保存了a的做用域链,因此也能够访问到 num ,执行 b() 后,加一;
  • 那为何第二次执行 demo(),打印出的值仍是有自增了呢?这是由于操做的都是保存在 b 里的 num ,虽然每次调用 demo() 都会造成新的做用域链,可是num,倒是每次从上一次的做用域链直接 copy到当前做用域链中的。

这样造成的闭包虽然可使外部能够访问到内部的函数,可是致使了原有的做用域链不释放,会形成内存泄漏。(内存泄漏的意思就是占用内存,可用内存资源变少了)。因此若是不是特殊须要,应尽可能防止这种状况发生。模块化

而且,做用域链的配置机制引出了一个值得注意的反作用:即闭包只取得包含函数中任何变量最后一个值,好比下面这个例子:函数

function createFunctions() {
     var result = [];
     for(let i = 0; i< 10; i++) {
         result[i] = function() {
             console.log(i);
          }
       }
    return result;
 }
var myArr = createFunctions();
for(var j = 0; j < 10; j++) {
       myArr[j]();
 }

这个函数会返回一个函数数组,表面上看,彷佛每一个函数都应该有本身的索引值,即会返回:0,1,2...9;但实际上,每一个函数都会返回10;这是由于在createFunctions()执行时,for循环跳出的条件是i=10;因此函数返回后,i的值是10 ;而每一个result的做用域链中都保存这createFunctions()的AO,因此他们引用的都是createFunctions()的i值,因此每一个函数内部i的值都是10;
这样,咱们能够建立一个当即执行函数强制让闭包的行为符合预期:spa

function createFunctions() {
     var result = [];
     for(var i = 0; i < 10; i++) {
           (function(j) {
             result[i] = function() {
                document.write(j + " ");
                }
          }(i));
     }
    return result;
}
var myArr = createFunctions();
for(var j = 0; j < 10; j++) {
       myArr[j]();
 }

典型应用

下面看看几个用到闭包的典型例子:设计

  • 实现共有变量

    如累加器:调用多少次,累加多少次,用闭包更加模块化

function add() {
            var count = 0;
            function demo() {
                count++;
                console.log(count);
            }
            return demo;
        }
        var counter = add();
        counter();//1
        counter();//2
        counter();//3
  • 实现缓存

如eater: eat和push保存的都是eater的AO;,因此eat中food改变后。其实是eater变了,因此也会影响push;

function eater() {
            var food = '';
            var obj = {
                eat: function() {
                    console.log('eating' + food);
                    food = '';
                },
                push: function(myFood) {
                    food = myFood;
                } 
            }
            return obj;// 至关于返回里面的eat和push操做food;
        }
        var eater1 = eater();
        eater1.push('banana');
        eater1.eat();
相关文章
相关标签/搜索