作用域链和闭包

270 阅读1分钟

闭包

一个经典的循环问题

function a(){
    for(var i=0;i<10;i++){
        setTimeout(()=>{
            console.log(i)
        })
    }
}

a()
输出:
10
10
10
10
10
10
10
10
10
10

以上输出都是10,为什么呢?

这是因为var是函数作用域,所以以上函数和下面的是一致的:

function a(){
    var i=0
    for(;i<10;i++){
        setTimeout(()=>{
            console.log(i)
        })
    }
}

a()

闭包是如何解决这个问题的呢?

既然是作用域链的问题那么延长就可以了加一个函数包起来不就得了,通过作用域链将这个i延长到每一次循环包裹的函数当中。

function a(){
    for(var i=0;i<10;i++){
        (function (i){
            setTimeout(()=>{
                console.log(i)
            })
        })(i)
        
    }
}

a()
输出
0
1
2
3
4
5
6
7
8
9

还有一种方式是let 这个是因为块级作用域相当于变相延长作用域了。

所以闭包的本质还是作用域链,作用域链在函数定义的时候就关联起来,所以类似返回一个函数,事件回调等都是在编译的时候就确定好作用域链,所以这个函数返回到外部或者被外部调用比如事件回调,这个函数的作用域链还是编译时期就确认好的,所以才可以访问到内部变量以及定义是所在的上下文,当然由于js的引用计数内存回收,所以闭包就可以保存住内部的变量了。

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