【Javascript】速来看小白级别的闭包知识初了解

本文介绍了JavaScript中的闭包概念,通过经典案例解释了闭包如何捕获外部作用域的变量,并强调了它们在函数定义与调用作用域不同时的重要性。闭包在JavaScript中的应用包括创建私有变量、实现计数器等功能,是理解和掌握JavaScript高级特性的关键。
摘要由CSDN通过智能技术生成

引出闭包

开始之前,先来看看下面的一个经典例子,试着自己想出结果:

let sample = "global scope"
function checkscope(){
    let sample = "local scope"
    function f() {return sample}
    return f()
}
checkscope()

聪明的你一定想到了

checkscope()函数声明了一个局部变量,然后定义了一个返回该变量的值的函数并调用了该函数。很显然,调用checkscope()应该返回“local scope”

我们知道:JavaScript函数可以嵌套定义在其他函数里,内嵌的函数可以访问定义在函数作用域的任何变量。事实上,与多数现代编程语言一样,JavaScript使用词法作用域(lexical scoping)

这意味着函数执行时使用的是定义函数时生效的变量作用域,而不是调用函数时生效的变量作用域。

为了实现词法作用域,JavaScript函数对象的内部状态不仅要包括函数代码,还要包括对函数定义所在作用域的引用

这种函数对象作用域(即一组变量绑定)组合起来解析函数变量的机制,在计算机科学文献中被称作闭包(closure)

浅浅了解闭包

有细心的小伙伴看这上面这句话,发现了端倪:

“多数函数调用与函数定义都在同一作用域内,所以闭包有什么用?”

闭包真正值得关注的时候,是定义函数与调用函数的作用域不同的时候。最常见的情形就是一个函数返回了在它内部定义的嵌套函数。很多强大的编程技术都是建立在这种嵌套函数闭包之上的,因此嵌套函数闭包在JavaScript程序中也变得比较常见。乍一接触闭包难免不好理解,但只有真正理解了,才能用好它们。

还记得最开始的那个经典案例吗,其实他还有“二段式”

let sample = "global scope"
function checkscope(){
    let sample = "local scope"
    function f() {return sample}
    return f
}
checkscope()() // => f()

我猜有人会说:“这题我会,必是global scrope

其实不然:此时刚好函数调用与函数定义不在同一作用域内。而JavaScript函数是使用定义它们的作用域来执行的。在定义嵌套函数f()的作用域中,变量sample绑定的值是“local scope”,该绑定在 f 执行时仍然有效,无论它在哪里执行

因此前面代码示例最后一行返回“localscope”,而非“global scope”。简言之,这正是闭包惊人且强大的本质:

它们会捕获自身定义所在外部函数的局部变量(及参数)绑定

再看看下面另外一个例子:

let hsiao =(function(){
    let counter = 0;
    return function(){ return counter++}
}())
hsiao()  // 0
hsiao()  // 1

作为小白的我当时看到也有点懵,不过不怕,我们慢慢捋

首先我们要知道:第一行代码乍一看像一条赋值语句,把一个函数赋值给变量hsiao。实际上(如第一行代码开头的左圆括号所提示的),这行代码定义并调用了一个函数,就是立即执行函数

(function(){
   ... 
})();//更清晰
​
(function(){
   ...
}());//w3c建议   

因此真正赋给hsiao的是这个函数的返回值。再仔细看一下函数体,你会发现它的返回值是另一个函数function(){return counter++}

换句话说,这个嵌套的函数最终被赋值给了hsiao。这个嵌套函数有权访问其作用域中的变量,而且可以使用定义在外部函数中的变量counter。外部函数一旦返回,就没有别的代码能够看到变量counter了,此时内部函数拥有对它的专有访问权

类似counter这样的私有变量并非只能由一个闭包独享。同一个外部函数中完全可以定义两个或更多嵌套函数,而它们共享相同的作用域。

这就厉害了,那不妨再看看下面这个例子:

function counter(){
    let n = 0;
    return{
        count:function(){return n++};
        reset:function(){n = 0}
    };
}
let c = counter(),d=counter()
c.count() //0
c.count() //1
d.count() //0
c.reset()
c.conut() //0
d.count() //1

这个counter()函数返回一个“计数器”对象。该对象有两个方法:count()reset()。前者返回下一个整数,后者重置内部状态。

首先要理解的是,这两个方法都有权访问私有变量 n 。其次是要知道,每次调用counter()都会创建一个新作用域(与之前调用创建的作用域相互独立),还有作用中域中的一个新私有变量。因此如果调用两次counter(),就会得到拥有两个不同私有变量的计数器对象。在一个计数器上调用count()reset()不会影响另一个计数器。

小总结

本文只是对闭包进行一个初体验,在了解闭包是什么的基础上浅浅地了解一下闭包的经典案例。明白这种函数对象作用域(即一组变量绑定)组合起来解析函数变量的机制。当然闭包还有更多高级的用法,待我学成归来继续探索。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

chapitea

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

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

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

打赏作者

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

抵扣说明:

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

余额充值