• 闭包概念集合


    一、创建闭包

    创建闭包的常见方式,就是在一个函数内部创建另一个函数。

    二、作用域链

    当某个函数被调用的时候,会创建一个执行环境和相应的作用域链,然后使用arguments初始化对象。这个对象叫做活动对象。

    在作用域链中,外部函数的活动对象始终处于第二位。以此类推,直到作用域链终点——全局执行环境。 首先,让我先来看看什么叫做活动对象。

    function compare(a,b){
        if(a>b){
            return true;
        }else if(a<b){
            return false;
        }else{
            return 0;
        }
    }
    var result=compare(5,10);//false
    console.log(result);

    这段代码中,首先定义了compare()函数,然后在全局作用域中调用了它。在调用这个函数的时候,创建了arguments、a、b这三个活动对象。他们处在作用域链的第一位。

    而result、compare被称为变量对象,他们处于全局执行环境下,在作用域链中处于第二位。

    全局环境的变量对象始终存在,在函数中访问一个变量时,就会在作用域链中寻找具有相应名字的变量。函数执行完毕,活动对象就被销毁。而闭包的特殊就在于此,活动对象没有被销毁!

    看这段代码:

    function compare(propertyValue){
        return function(obj1,obj2){
            var a=obj1;
            var b=obj2;
            if(a<b){
                return true;
            }else if(a>b){
                return false;
            }else{
                return 0;
            }
        }
    }
    var fun=compare("value");
    console.log(typeof fun);//function
    var result=fun(5,10);
    console.log(result);//true

    函数compare()中包含了一个匿名函数,那么,该匿名函数,也就是闭包的作用域链中,就会有compare()函数的活动对象。因此,该闭包作用域链其实有三节:

    第一节,闭包的活动对象,obj1,obj2 和arguments;

    第二节,compare()函数的活动对象,arguments和propertyValue;

    第三节,全局变量对象,compare。

    这意味着什么意思呢?就是在compare()这个函数在执行完毕以后,他的活动对象propertyValue和arguments也不会被销毁。因为匿名函数的作用域链仍然在引用这个活动对象。也就是说,compare()执行完以后,其执行环境的作用域链会被销毁,但是他的活动对象却不会被销毁。除非匿名函数被销毁。

    而在js中,内置的工具函数setTimeout,往往就会造就一个闭包。

    function wait(message){
        setTimeout(function timer(){
            console.log(message);
        },1000);
    }
    wait('5');//5

    timer()函数就是一个闭包,即使wait()执行1000毫秒后,内部作用域仍然不会消失。timer具有wait()作用域的闭包。

    总结:由于闭包会携带外部函数的作用域,所以它会占用更多的内存。建议不要过多使用闭包,会导致内存占用过多。

    三、闭包与变量

    function fun(){
        var result=new Array();
    
        for(var i=0;i<4;i++){
            result[i]=function(){
                return i;
            };
        }
        return result;
    }
    var a=fun();
    console.log(a);//Array(4) [, , , ]

     好吧,这段代码我还是看的半懂不懂。似乎理解他为什么会返回这个,似乎又不理解。而且和书上写的也不一样啊。也没返回4个“3”啊!这是为啥内?

    for(var i=0;i<2;i++){
        setTimeout(function timer(){
            console.log(i);
        },i*1000);
    }
    console.log('daoda')//daoda;2;2

    看到没有,竟然先输出了"daoda",然后又输出了两个“2”!

    首先解释“2”是怎么来的。首先,这个循环终止的条件是i不再小于2,也就是说,条件首次成立时,i的值为2。因此,输出显示的是循环结束时i的值。

    延迟函数的回调是在循环结束的时候才执行。即使执行的是setTimeout(...,0),所有的回调函数依然是在循环结束后才会执行。因此每次输出的都是2!

    你以为每次迭代都会有对应的i,但是根据作用域的原理,尽管循环中的5个函数是在各个迭代部分中分别定义的,但是它们却都被封闭在一个共享的全局作用域中,因此实际上只有1个i。

    for(var i=0;i<2;i++){
        (function(){
            setTimeout(function timer(){
                console.log(i);
            },i*1000);
        })();
    }
    console.log('蓝色橙汁');//"蓝色橙汁";2;2

    IIFE会立即执行函数,为什么输出的还是两个“2”呢?

    这是因为IIFE的作用域是空的。他要有自己的变量,用来在每次迭代中存储i的值才行。

    代码写成这样才可以:

    for(var i=1;i<=5;i++){
        (function(){
            var j=i;
            setTimeout(function timer(){
                console.log(j);//1,2,3,4,5!这样就可以了
            },j*1000);
        })();
    }

    上面这段代码可以做一些改进:

    for(var i=1;i<=5;i++){
        (function(j){        
            setTimeout(function timer(){
                console.log(j);//1,2,3,4,5!这样就可以了
            },j*1000);
        })(i);
    }

    奇怪的是,为什么这里我就理解了?

  • 相关阅读:
    开启Chrome内核浏览器的多线程下载功能
    golang fasthttp
    国内外短信接码平台合集
    jsrsasign 进行 RSA 加密、解密、签名、验签
    sysbench对自装MySQL数据库进行基准测试
    MySQL慢查询
    Logstash的配置
    简单的MySQL主从状态监控
    aria2c备忘
    DataX 整合后重新编译打包
  • 原文地址:https://www.cnblogs.com/qingshanyici/p/10504625.html
Copyright © 2020-2023  润新知