继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

js之深入理解闭包问题。

ruibin
关注TA
已关注
手记 77
粉丝 9109
获赞 2572

最近一段时间准备好好学习下机器学习,所以了解了很多机器学习方面的知识。抓紧补救了下自己曾经遗忘的理论知识(高数、统计学、概率论、微积分等),发现还有点吃力了,很多东西都快忘得差不多。不过,咱程序员,还是很擅长围魏救赵滴^_^。所以,我果断的选择边理论边着手老本行——编程。

选择语言问题,就是第一大难题。

目前流行的机器学习语言大抵是R语言和python了,熟悉了下R,发现简直就不该是程序员该用的,这简直就是为非专业编程人士准备的东西。大概了解了下,就放弃了。那现在选择就一个了,python。

初学python,感觉如鱼得水啊。这对把函数式编程当成工作的我来说,学起来简直游刃有余啊,瞬间被微积分概率论虐得生活不能理解的感觉一去不复返~额,貌似扯远了。前奏有点太长了,下面进入正题。

闭包?

当我进入python的闭包时,看见了这么一道题。

# 希望一次返回3个函数,分别计算1x1,2x2,3x3:
def count():
    fs = []
    for i in range(1, 4):
        def f():
             return i*i
        fs.append(f)
    return fs

f1, f2, f3 = count()

这不就是曾经那个闭包吗?现在还记得第一次面试时,面试官问到我闭包时,我一番鬼扯然后用错误答案回答的尴尬。既然这样,我觉得有必要好好和大家分享下我心中的闭包。

闭包是什么呢?内层函数引用了外层函数的变量(参数也算变量),然后返回内层函数的情况,称为闭包(Closure)。

闭包的特点是返回的函数还引用了外层函数的局部变量。定义听起来简单易懂,能正确应用才是关键。上个题的答案是

def count():
    fs = []
    for i in range(1, 4):
        def f(i) :
            def g() :
                return i * i
            return g
        fs.append(f(i))
    return fs

f1, f2, f3 = count()
print f1(), f2(), f3()

为什么会这样?下面我们用js的方式来处理下另外一个题目,相信能处理这个新题目,你正确处理这个题目也是很简单的事。

时间再一次回到第一次面试。面试官出了这么一道经典题目:请使用闭包输出1-100每隔一秒输出一次。额,这个问题怎么这么熟悉呢?貌似在哪里见过,一百道经典面试题还是你必须知道的十道js面试问题来着~,想了半天,就知道要用闭包,要用setTimeout。好吧,有这些就够了,既然一时想不出来,那就直接写吧!

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

貌似有什么不对。这个大概会输出一百个100的节奏啊~,可是哪里不对呢?找了半天,没发现,那推倒重来!既然是闭包,那不是要return函数么,那再来过。

function closure() {
    for (var i = 0; i < 100; i ++) {
        setTimeout(function() {
            var foo = function(i) {
                return function() {
                    console.log(i);
                }
            }
            foo(i)
        }, 1000)
    }
}

这个么,好像没有输出值啊~,左思右想,重来。

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

这个像,嗯,不错,就是他了。然后愉快的将最终结果给面试官过目,面试官问了句,你确定这是我想要的结果?是的,我很确定!现在依旧记得当初的意气风发啊,年轻真好!

回来之后,把自己把程序在浏览器上跑了下,瞬间懵逼了。这不是我想要的结果啊,我想要的结果是100s!最后自己默默的在1000后面乘了个i,最终程序是这样的:

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

但是,为什么是这样呢?首先setTimeout是一个定时器,一个异步的定时器。什么意思呢?就是必须主程序跑完了,才开始执行它!意思就是你的for循环跑完了,才会执行setTimeout。所以如果直接输出,很明显,i的值为100.所以你会得到100个100.怎样才能保留i,让i按照我们想要的循环一下输出一下呢?那就使用闭包吧,把i传进去。这样i的值就被保留下来了,一直被内部的函数引用。所以就能输出我们想要的结果。最后为什么要乘以i呢,因为是一秒执行一次,不是过了一秒全部执行!所以~

以上就是内容的全部,如果还有不懂,请留言。我会关注的,谢谢。

打开App,阅读手记
6人推荐
发表评论
随时随地看视频慕课网APP