zoukankan      html  css  js  c++  java
  • 对闭包的理解

    首先看一段代码

    for(var i = 0; i < 10; i++) {
            console.log(i);
    }

    这段代码输出0, 1, 2, 3, 4, 5, 6, 7, 8, 9

    接着

    for(var i = 0; i < 10; i++) {
        setTimeout(function() {
            console.log(i);
        }, 1000);
    }

    这段代码输出的全是10

    这里引用原文的话:详情https://www.douban.com/note/293295975/

    因为setTimeout是异步的!
       你可以想象由于setTimeout是异步的, 因此我们将这个for循环拆成2个部分
       第一个部分专门处理 i 值的变化, 第二个部分专门来做setTimeout
       因此我们可以得到如下代码
       // 第一个部分
       i++;
       ...
       i++; // 总共做10次

       // 第二个部分
       setTimeout(function() {
          console.log(i);
       }, 1000);
       ...
       setTimeout(function() {
          console.log(i);
       }, 1000); // 总共做10次

       这样一拆后, 我相信你肯定知道之前那个for循环的运行结果了.
       由于循环中的变量 i 一直在变, 最终会变成10, 而循环每每执行setTimeout时, 其中的方法还没有真正运行, 等真正到时间执行时, i 的值已经变成 10 了!
       i 变化的整个过程是瞬间完成的, 总之比你异步要快, 就算你setTimout是0毫秒也一样, 会先于你执行完成.

    接下来上自己的例子

    var nodes = document.getElementsByTagName("li");
                for(i = 0;i<nodes.length;i+= 1){
                    nodes[i].onclick = function(){
                        console.log(i+1);//不用闭包的话,值每次都是4
                    };
                }

    这段代码无论点击哪个 li 都只输出4;

    这里我个人的理解,根据浏览器JS解析引擎;

    JS解析引擎一开始并不是直接从上到下开始执行代码

    而是搜集代码中的全局变量和函数块存起来。

    接着再从上到下执行代码。

    第一行代码

    nodes = document.getElementsByTagName("li");

    给变量nodes赋值,接着for循环

     for(i = 0;i<nodes.length;i+= 1){
                    nodes[i].onclick = function(){ console.log(i+1);};
                }
     nodes[i].onclick为点击事件添加处理函数(注意!function(){ console.log(i+1);}并没有执行,而是函数的引用赋值给nodes[i].onclick而已)
    所以当点击事件触发,浏览器执行  function(){ console.log(i+1);}时查找 i 的值就是for循环执行后的值。
    也可以理解成这样
      function a(){ console.log(i+1);};
    
    
     for(i = 0;i<nodes.length;i+= 1){
                    nodes[i].onclick = a;
                }
     

    这就是说明  console.log(i+1) 只输出循环的之后的值得原因在于:(!function(){ console.log(i+1);}并没立即执行)

    所以为了解决该问题  

    i 作为参数传入来固定这个变量的值, 让其保留下来

    需要作以下修改

    function a (i) {
            console.log(i);
        }
        for(var i = 0; i < 10; i++) {
        setTimeout(a(i), 1000);
        }

    这样已经可以输出0, 1, 2, 3, 4, 5, 6, 7, 8, 9了。

    再接着修改

    for(var i = 0; i < 10; i++) {
        setTimeout((function a (i) {console.log(i);})(i), 1000);
        } 
    var nodes = document.getElementsByTagName("li");
                for(i = 0;i<nodes.length;i+= 1){
                    nodes[i].onclick = (function(){
                        console.log(i+1);//不用闭包的话,值每次都是4
                    };)(i)
                }

    其实上诉解决办法就是闭包啦。

    不过这样也会带来副作用,例如setTimeout并没有延时就直接输出了,点击事件并没有点击也触发了。

     闭包的作用

    先上代码

    function say667() {
                // Local variable that ends up within closure
                var num = 666;
                var sayAlert = function() {
                    alert(num);
                }
                num++;
                return sayAlert;
            }
    
             var sayAlert = say667();
             sayAlert()//执行结果应该弹出的667

    执行say667()后,say667()闭包内部变量会存在,而闭包内部函数的内部变量不会存在
            使得Javascript的垃圾回收机制GC不会收回say667()所占用的资源
            因为say667()的内部函数的执行需要依赖say667()中的变量
            这是对闭包作用的非常直白的描述

  • 相关阅读:
    107. Binary Tree Level Order Traversal II
    108. Convert Sorted Array to Binary Search Tree
    111. Minimum Depth of Binary Tree
    49. Group Anagrams
    使用MALTAB标定实践记录
    442. Find All Duplicates in an Array
    522. Longest Uncommon Subsequence II
    354. Russian Doll Envelopes
    opencv 小任务3 灰度直方图
    opencv 小任务2 灰度
  • 原文地址:https://www.cnblogs.com/liucanhao/p/5713710.html
Copyright © 2011-2022 走看看