关于闭包的理解

一、闭包的概念

1、多种概念多种理解

当函数可以记住并访问所在的词法作用域的时候,就产生了闭包,即使函数是在当前词法作用域之外执行

闭包就是能够读取其他函数内部变量的函数,可理解成“定义在一个函数内部的函数”

闭包让你可以在一个内层函数中访问到其外层函数的作用域

来看这段代码:

function foo() {
    var a = 2
    function bar() {
        console.log(a);
    }

    bar()
}

foo()

上面这段代码,从技术上讲,也许是闭包,但是从上面的定义上来讲并不是闭包,上面最准确地用来解释bar()对a的引用的方法是词法作用域的规则,而这些规则只是闭包的一部分(但却是非常重要的一部分)

而下面的这段代码清晰的展示了闭包:

function foo() {
    var a = 2

    function bar() {
        console.log(a);
    }

    return bar  //将bar()函数本身当作一个值类型进行传递
}

var baz = foo()
baz()//2

不光可以通过return的形式,还可以通过函数传参的形式:

function foo() {
    var a = 2

    function baz() {
        console.log(a);
    }

    bar(baz) //将baz()当成了一个函数的参数在外调用
}

function bar(fn) {
    fn()
}

foo()

二、深入理解

只要使用了回调,就是使用了闭包

1、关于for循环

function foo() {
    for(var i = 1; i <= 3; i++) {
        setTimeout(function timer() {  //这里使用了一个回调,形成了一个闭包
            console.log(i);
        },i*1000)
    }
}

foo()//会一秒一次的输出3个4

为何不是我们预期的一秒一次地分别输出1,2,3?

当定时器运行的时候即使每个迭代中执行的是setTimerout(…,0),所有的回调函数依然是在循环结束后才会被执行,因此每次会输出一个4,

我们认为每次的1,2,3的输出,是我们认为循环中每个迭代在运行的时候都会自己捕获一个 i 的副本,但是根据作用域的原理,实际上是

尽管循环中的三个函数是在每次迭代中分别定义的,但是它们都被封闭到一个共享的全局作用域,因此实际上只有一个 i

将上面的代码进行原理解析:

function foo() {
    var i =1
    setTimeout(function timer() {
        console.log(i);
    },1000)
    var i =2
    setTimeout(function timer() {
        console.log(i);
    },2000)
    var i =3
    setTimeout(function timer() {
        console.log(i);
    },3000)

}
foo()
//我们知道计时器setTimeout是异步里面的宏任务,是在主线程执行完之后,再执行的,所以我们再分析可以将上面的代码解析成:

function foo() {
    var i =1
	var i =2
	var i =3
	
    setTimeout(function timer() {
        console.log(i);
    },1000)
    
    setTimeout(function timer() {
        console.log(i);
    },2000)
    
    setTimeout(function timer() {
        console.log(i);
    },3000)

}
foo()

2、我们如何使用闭包来解决这个问题?

解决思路:我们需要更多的闭包作用域,特别是在循环过程中的每个迭代都需要一个闭包作用域

(1)在迭代里使用立即执行函数 来为每个迭代都生成一个新的作用域,使得延迟函数的回调可以将新的作用域封闭在每个迭代内部,每个迭代中都会含有一个具有正确值变量来供我们访问

function foo() {
    for(var i = 1; i<= 5; i++) {
        (function(j) {   //迭代中每次立即执行函数的执行都会创建一个新的函数作用域
            setTimeout(function timer() {
                console.log(j);
            }, j*1000)
        })(i)
    }
}

foo()

(2) 更简单的方法

function foo() {
    for(let i = 1; i <= 3; i++) {//let  将变量绑定到其所在的定义域
        setTimeout(function timer() { 
            console.log(i);
        },i*1000)
    }
}

foo()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值