深入javascript之作用域闭包(八)

前言

js中闭包无处不在,你只需要识别并拥抱他,闭包是基于词法作用域书写代码时所产生的自然效果,你甚至不需要为了利用他们而有意的创建闭包。

实质问题

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

// 闭包是基于词法作用域书写代码时所产生的自然效果
// 当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行
function foo() {
    var a = 2
    function bar() {
        console.log(a);
    }
    bar()
}
foo()

基于词法作用域的查找规则,函数bar可以访问外部作用域中的变量a(这个作用域中是一个RHS查询)
技术上来讲,也许是闭包,但是并不是。我认为最准确的来解释bar(),对a的引用的方法是词法作用域的查找规则,而这些规则只是闭包的一部分。
从学术角度来说,上面的代码片段中,函数bar()具有一个涵盖foo函数作用域的闭包。也可以认为bar()封闭在了foo()作用域中,即bar()嵌套在了foo()内部。

但是通过这种方式定义的闭包不能直接观察,也无法明白这个代码片段中闭包是如何工作的。我们可以很容易的理解词法作用域,而闭包则是隐藏在代码之后的神秘阴影里面,并不那么容易理解。

下面看一下清晰的闭包:

function foo() {
    var a = 2
    function bar() {
        console.log(a);
    }
    return bar
}
var baz = foo()
baz()//2 实际上只是通过不同标识符引用调用了每部的函数bar()
// bar()依然有对该作用域的引用,而这个引用就叫做闭包

bar在自己定义的词法作用域以外的地方执行的。

在foo执行后通常会由于js引擎的垃圾回收机制来释放不再使用的内存空间,即foo整个内部作用域都被销毁。

但是闭包正阻止内存回收,也就是内部作用域依旧存在,那谁在使用这个内部作用域呢?
其实就是bar本身在使用

拜bar的位置所赐,它拥有涵盖foo()内部作用域的闭包,使得该作用域能够一直存活,以供bar在之后任何时间进行引用。

bar()依然有对该作用域的引用,而这个引用就叫做闭包。

当然,以何种方式对函数类型的值进行传递,当函数在别处被调用时都可以观察到闭包:

直接传递函数

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

function bar(baz) {
    baz()//这里就是闭包
}

foo()

//一个函数的调用,是在其他作用域中

把函数诶不baz传递给bar,当调用这个内部函数时(fn),它涵盖的foo()内部的作用域的闭包就可以观察到了,因为它能够访问a。

间接传递函数:

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

}
function bar() {
    fn()
}
foo()
bar()

无论通过何种手段将内部函数传递到所在的词法作用域以外,他都会持有对原始定义作用域的应用,无论在何处执行这个函数都会使用闭包。

真正明白

前面的代码有些死板,为了解释闭包而刻意进行修饰

function wait(message) {
    setTimeout(function timer() {
        console.log(message);
    }, 1000)
}
wait('hello')

timer具有涵盖wait(…)作用域的闭包,因为还保持对变量message的引用。

在引擎内部,内置的工具函数setTimeout(…)持有对一个参数的引用,引擎会强调这个函数,在例子中就是内部的timer,而词法作用域在这个过程保持完整,这就是闭包

在这里插入图片描述

富有争议的闭包
var a = 2
    (function IIFE() {
        console.log(a);
    })()

循环和闭包
for (var i = 1; i <= 5; i++) {
    setTimeout(function () {
        console.log(i);
    }, 1000)
}
//一下子打印出来五个6
for (var i = 1; i <= 5; i++) {
    (function () {
        setTimeout(function () {
            console.log(i);
        }, i * 1000)
    })()
}
//五个6
for (var i = 1; i <= 5; i++) {
    (function () {
        var j = 1
        setTimeout(function () {
            console.log(i);
        }, i * 1000)
    })()
}
//五个6
for (var i = 1; i <= 5; i++) {
    (function (j) {
        setTimeout(function () {
            console.log(j);
        }, 1000)
    })()
}
//五个undefined
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三千洲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值