• 对闭包的理解


    首先看一段代码

    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()中的变量
            这是对闭包作用的非常直白的描述

  • 相关阅读:
    【转】EditText获取焦点不自动弹出键盘设置--失去焦点的方法,不错
    【转】eclipse android 设置及修改生成apk的签名文件 -- custom debug keystore
    【转】Adnroid4.0 签名混淆打包(conversion to dalvik format failed with error 1)
    【转】Android Fragment 基本介绍--不错
    【转】java代码中实现android背景选择的selector-StateListDrawable的应用
    【转】android官方侧滑菜单DrawerLayout详解
    【转】微信Android SDK示例代码及运行方法
    【转】Eclipse中设置ButterKnife进行注解式开发步骤 -- 不错
    【转】ButterKnife基本使用--不错
    python 命令行:help(),'more'不是内部或外部命令,也不是可运行的程序或批处理文件
  • 原文地址:https://www.cnblogs.com/liucanhao/p/5713710.html
Copyright © 2020-2023  润新知