定义

当函数可以记住并访问它定义时候的作用域,就产生了闭包。也就是说,一个函数在另外一个函数里面定义,但是它执行的时候,可能是在其他的作用域,但是它还是能够凭着出生时的回忆,找到定义时的作用域,去访问里面的变量。

    function foo() {
        var a = 2;
        function bar() {
            console.log(a)
        }
        return bar;
    }
    
    var bar = foo();
    bar();//记住了定义时的作用域,所以还是可以访问到a,这里会输出2

为什么能够访问到定义时候的作用域呢?答案只有一个,定义闭包的函数是个重情义的家伙,考虑到那个宝宝可能随时要用到它里面的变量,都不肯被回收。没错,这样会导致foo的作用域一直没有被回收,有一点浪费内存空间。

关于this对象

一般函数的this指向的是调用它的对象。由于闭包函数是由window调用的,所以一般它的this指向window。如果你需要让它指向某个对象,可以用call或者把外部的this保存起来,在里面直接使用。

    var name = 'window';
    var object = {
        name: 'My Object',
        //var that = this;
        getNameFunc: function() {
            return function() {
                console.log(this.name);
                //console.log(that.name);
            }
        }
    }
    
    object.getNameFunc()();//输出'window'
    object.getNameFunc().call(object);//输出'My Object'

循环和闭包

很常见的一段代码,运行结果就是输出5个5。因为这里的i没有块级作用域,也就是说闭包里面的函数访问到外部的变量i只有一个,加上setTimeout是异步的,执行到里面的回调的时候,外面的这个i的值就是循环结束后的值5。

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

在没有let的时候,我们都是通过模仿块级作用域来解决。这个时候,闭包函数定义的作用域变成了(function(){}),然后在这个作用域里面把i的值赋值给j,每个闭包函数访问到的j就是它定义时候作用域已经保存好了的,自然就能够正确的输出。

     for(var i = 0; i < 5; i++) {
         (function() {
            var j = i;
            setTimeout(function() {//闭包
                console.log(j);
            }, 1000)
         })()
    }

当然,我们一般都舍不得再用一个j来保存,直接用传参数的形式,让块级作用域可以访问到i的值。

     for(var i = 0; i < 5; i++) {
         (function(j) {
            setTimeout(function() {//闭包
                console.log(j);
            }, 1000)
         })(i)
    }

简单改一下,每次用个块级作用域的let来保存一下是不是就可以了啊

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

哈哈哈,真的就输出0 1 2 3 4 5了。因为闭包记住了它定义时候的作用域呀,这个时候的j是块级的,能够好好地保存起来被闭包函数使用。
其实就是相当于每次循环都会去声明一个j,然后这个j的作用域就属于这次循环,所以可以直接把j的赋值写在for循环里。

    for(let j = 0; i < 5; i++) {
        setTimeout(function() {
            console.log(j);
        }, 1000)
    }

模块化

可以使用闭包来实现模块化的。

    var module = (function(window) {
        function test1() {
        }
        functon test2() {
        }
        
        return {
            test1: test1,
            test2: test2
        }
    })(window)

supportlss
230 声望16 粉丝