JavaScript闭包

目录

变量作用域

闭包的概念

深入了解函数程序的执行

闭包的基本原理

闭包的注意点


闭包是JavaScript的一大难点,也是它的特色。很多高级应用都要依靠闭包来实现。

变量作用域


要理解闭包,首先要理解JavaScript的特殊的变量作用域。

变量的作用域无非就两种:全局变量和局部变量。

JavaScript语言的特别之处就在于:函数内部可以直接读取全局变量,但是在函数外部无法读取函数内部的局部变量。

全局变量的作用域是全局性的,即在整个JavaScript程序中,全局变量处处都在。

而在函数内部声明的变量,只在函数内部起作用。这些变量是局部变量,作用域是局部性的;函数的参数也是局部性的,只在函数内部起作用。

闭包的本质就是在函数外部 调用操作 函数内部的数据。

为了保护数据的安全性,全局变量容易造成全局变量污染,为了确保数据的安全性会将重要的数据定义成局部变量。

再通过闭包的语法形式调用使用函数内部的数据。

闭包的概念


由于实际项目的需求,我们有时候需要获取到函数内部的局部变量。正常情况下,这是办不到的。这时候我们就要想办法把函数内部的局部变量取出来,如下所示:

function fun(){
   let int = 2;
   
   return function(){
      console.log(int);//2
      int *= 2 ;
      console.log(int);//4
   }
}

上面代码中return的匿名函数,就是闭包。

由于在 JavasSript 中,只有函数内部的子函数才能读取局部变量,所以说,闭包可以简单理解成"定义在一个函数内部的函数"。

闭包可以用在许多地方,它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持再内存中,不会在调用后被自动清除。

要理解闭包的第二个作用,我们就需要深入了解函数程序的执行。

深入了解函数程序的执行


首先我们先要了解一下栈和堆,在 内存中 有 两个 存储区域(栈和堆) 存储数据。


栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放。存储的是:

基本数据类型:布尔、数值、字符串、undefined、null。

有序存储数据:先定义的数据存储在栈的下方,后定义的数据存储在栈的上方。


堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS(操作系统)回收,分配方式倒是类似于链表。

堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。存储的是:

引用数据类型:函数、数组、对象。

无序存储数据:在 堆 中 开辟一个 独立的 存储空间 准备存储 引用数据类型数据,操作系统 会给这个存储空间 分配一个 内存地址,引用数据类型 变量/函数名称 存储在 栈 中,其中 存储的是 存储空间的 内存地址。

以上内容,可以用下图来解释说明一下:
函数程序的执行

封装

1、在内存的堆中开辟一个存储空间,操作系统给这个存储空间赋值内存地址,存储空间准备存储函数程序。

2、函数程序以字符串形式存储在存储空间中。

3、函数名称/变量名称 存储在内存的栈中,函数名称/变量名称 中存储的是函数的内存地址

调用执行。

1、通过栈中的 函数名称/变量名称 中存储的内存地址,找到堆中的存储空间,调用其中存储的函数的代码程序。

2、给函数的形参赋值实参。

3、预解析函数的程序

4、执行函数程序代码

函数执行开始

在堆中函数的存储空间中,再开辟一个独立的内存空间称为执行空间,专门存储函数执行时,需要的形参 变量等

函数执行结束

执行空间自动自动 销毁/释放,执行空间中存储的形参 变量等也会一起 销毁/释放。

这个过程就被称为JavaScript内存回收机制。

以上内容,可以用下图来解释说明一下:

闭包的基本原理


1、不能调用函数内部数据的原因是,函数执行结束执行空间就会被释放,其中存储的形参和变量也会被释放;如果要操作调用函数内部的形参和变量就需要执行空间不会被销毁/释放,就需生成一个不会被销毁/释放的执行空间,那就是 给函数一个返回值,函数的返回值是一个引用数据类型。如下所示:

function fun(){
    const arr = [1, 2, 3, 4];
    return arr;
}
const res = fun();

res中存储的是函数fun的执行结果返回值,返回的是arr中存储的数组的内存地址,数组arr的内存地址在函数外被变量存储,也就是数组arr在函数外被使用,此时函数的执行空间不会被销毁/释放,但是此时还不能直接操作变量arr。

2、看下面的代码

function fun(){
   let int = 2;
   return function(){
      int *= 2;
      if( int > 300){
         console.log('王昭君');
      }else{
         console.log('王昭没有君啊');
      }
   }
}

const res = fun();
res();

int定义在函数内部,不会被全局变量污染,数据就安全了。

函数的返回值是一个匿名函数,return返回的是一个引用数据类型,执行空间就不会被 销毁.释放,变量int和return的匿名函数就会一直存在。

res中存储的是函数fun的返回值,也就是return的匿名函数。

调用res()就是执行调用匿名函数,也就是对变量int数据的操作调用。

闭包的注意点


1、由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在浏览器中导致内存泄漏。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

2、闭包会在函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象使用,把闭包当作它的公用方法,把内部变量当作它的私有属性,这时一定要小心,不要随便改变父函数内部变量的值。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

王昭没有君啊

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

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

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

打赏作者

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

抵扣说明:

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

余额充值