zoukankan      html  css  js  c++  java
  • javascript中的闭包

    前言

      本篇是基于对 《你不知道的JavaScript(上卷)》中的第五章的总结理解。

    不完全正确的概念

      简单通俗的说,函数内嵌函数就是闭包。但不完全正确,最重要的是内部函数执行时仍然持有外层作用域内的引用。

    闭包解释

    function outer() {
            var a = 2
            function inner(){
                console.log(a)
            }
    
            return inner
    }
    
    var fun = outer()
    fun()

      上述代码就是一个闭包。

    1. 内部函数 inner 的作用域包括自身作用域外还向外涵盖,所以 inner 内可以使用 outer 内的变量 a;
    2. outer 函数将 inner 函数作为返回值。

      一般来说,outer 函数执行完后,内部的变量 a 本来要被垃圾回收,但是其内部的函数 inner 有在使用 变量 a,为了 fun(也就是 inner ) 能够何时何地的正确执行,所以变量 a 不会被回收(保留了outer内的作用域)。这就是闭包。再看:

    var fun
        function outer() {
            var a = 2
            function inner(){
                console.log(a)
            }
    
            fun = inner
    }
    
    outer()
    fun()

      上述代码也是闭包。将内部函数 inner 赋值给fun。为了 inner 能够正确执行,变量 a 没有被回收。

      所有,无论使用何种方法将内部函数传递到外层作用域,内部函数仍然保持着原始作用域的引用(没有被回收),就会产生闭包。

      再看其他:

    function wait(num) {
            setTimeout(function timer(){
                console.log(num)
            }, 1000)
    }
    wait(1)

      这也是闭包,不管执行1秒后,timer 内部仍然引用着num, 因为 setTimeout 会持有对 timer 的引用,并没有 clear。所以 wait 内的作用域会保留下来。

      

    function click(value) {
            $(selector).click(function(){
                alert(value)
            })
    }
    
    click('hello')

      这也是闭包,因为 $(selector).click 绑定的函数内持有 click 的value,而 $(selector).click 执行后并没有解绑,所以为了保证正确运行,会保留 click 内的作用域。

    function outer () {
            var a = 2
            function inner () {
                console.log(a)
            }
            inner()
    }
    outer()

      上述代码,虽然函数内嵌了函数,但严格来讲并不是闭包。inner 执行后,并没有持有变量 a 的引用。outer 内的作用域可能已经被回收了。

    经典问题

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

      上述代码会打印 5 次 6。

      首先,javascript是单线程的,所以setTimeout(异步)会等for循环完了才会执行,而 for 循环完时, i 的值是6。但这并不足以导致setTimeout每次都打印出 6 。

      更重要的是每个(5个)setTimeout对 i 的引用,因为 for 中使用 var i 声明,所以 i 是在全局作用域内,每个setTimeout都引用了同一个 i。

      所以才会每次都打印出 6。

      相当于:

        var i = 6
    
        setTimeout(function timer(){
            console.log(i)
        }, i )
    
        setTimeout(function timer(){
            console.log(i)
        }, i )
    
        setTimeout(function timer(){
            console.log(i)
        }, i )
    
        // .....
    • 解决方案

        只要隔离每个setTimeout对 i 的引用即可。

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

      上面三种本质都是隔离每个setTimeout对 i 的引用。IIFE内自成一个作用域,将 i 赋值给其他变量,就隔离了对 i 的引用了。

      而使用 let,虽然 let 没有用在 {...}内,但 let 在for头部中有特殊行为,这个行为会将 i 多次声明,每迭代一次,声明一次,每次迭代都会使用上一次迭代结束时的值来初始化 i 。

      相当于:

        // 这里块作用域指 IIFE 内的作用域
        {
            var j = 1
            setTimeout(function timer(){
                console.log(j)
            }, j )
        }
        // 这里块作用域指 IIFE 内的作用域
        {
            var j = 2
            setTimeout(function timer(){
                console.log(j)
            }, j )
        }
        // ......
    
        /////////////////////////////////
    
        {
            let i = 1
            setTimeout(function timer(){
                console.log(i)
            }, i )
        }
    
        {
            let i = 2
            setTimeout(function timer(){
                console.log(i)
            }, i )
        }
        // ......
    Welcome to my blog!
  • 相关阅读:
    HDU 1010 Tempter of the Bone(DFS剪枝)
    HDU 1013 Digital Roots(九余数定理)
    HDU 2680 Choose the best route(反向建图最短路)
    HDU 1596 find the safest road(最短路)
    HDU 2072 单词数
    HDU 3790 最短路径问题 (dijkstra)
    HDU 1018 Big Number
    HDU 1042 N!
    NYOJ 117 求逆序数 (树状数组)
    20.QT文本文件读写
  • 原文地址:https://www.cnblogs.com/blogCblog/p/14450242.html
Copyright © 2011-2022 走看看