浅谈JavaScript闭包,小白的JS学习之路!

news2024/11/15 18:00:30

前言

在JavaScript中,闭包是一种强大而灵活的特性,它不仅允许变量私有化,而且提供了一种在函数执行完毕后仍然保持对外部作用域变量引用的机制。本文将深入讨论JavaScript闭包的概念、优点、缺点以及如何避免潜在的内存泄漏问题。

调用栈与作用域链

在理解闭包之前,首先需要了解调用栈和作用域链的概念。

调用栈

调用栈是用来管理函数调用关系的数据结构。当一个函数执行时,会将其执行上下文推入调用栈,如下图所示:

image.png

image.png

image.png

当函数执行完毕后,它的执行上下文就会从调用栈中弹出,如下图:

image.png

作用域链

作用域链是通过词法作用域(静态作用域)来确定某个作用域的外层作用域,在查找变量时,会按照由内而外的链状关系进行查找。这种链状关系叫做作用域链

  • 对于使用 var 声明的变量,它们位于变量环境。
  • 对于使用 letconst 声明的变量,它们位于词法环境。
  • outer属性指向外层作用域,全局执行上下文的outer指向null
  • outer的值取决于函数声明在何处而非在何处调用

如下图:

image.png

从bar的执行上下文到全局执行上下文以及foo的执行上下文到全局上下文的这种查找的链状关系,就叫做作用域链。

闭包的概念

闭包是指能够访问其外部函数中声明的变量的函数,即使外部函数执行完毕。在JavaScript中,由于词法作用域的存在,内部函数总是可以访问外部函数中声明的变量。我们来看下一个例子:

function foo() {
    var myName ='旭旭'
    let test1 = 1 
    let test2 = 2
    var innerBar = {
        getName:function(){
            console.log(test1)
            return myName
        },
        setName:function(newName){
            myName = newName
        }
    }
    return innerBar
}

var bar = foo()
bar.setName('浪哥')
console.log(bar.getName());

在上面的例子中,foo函数在执行完毕后,产生了一个闭包,内容为myName = '旭旭'test = 1,当foo()执行完成后,垃圾回收机制将foo的执行上下文清理掉了,但是由于,foo函数中的innerBar对象中的,getName函数以及setName函数中存在对test1myName的引用,所以在垃圾回收机制执行后,留下了myName = '旭旭'test = 1,他们的集合称作闭包。即,下图黄框部分:

image.png

闭包的简单应用

我们先来看这样一段代码:

 
var arr = []
for (var i = 0; i < 10; i++) {

  arr[i] = function () {
    console.log(i)
  }
} 

//------
for (var j = 0; j < arr.length; j++) {
    arr[j]()
}

代码看上去,像是要完成输出0-9的功能,但是实际上的输出结果为1010,因为在for循环声明的i是由var声明的,var存在声明提升,所以相当于在全局声明的i,而当第一个for循环结束后,i达到了10,并且声明了10个函数体,存到了数组arr[]中,在第二次的for循环中,将arr10个函数体取出并且调用,调用结果为打印i,而此时的i10,所以会输出1010

image.png

如果我们要在改动最小的情况下,使它的功能变为打印0-9那么我们可以将第一个for循环中的i,改为使用let声明

image.png

因为使用let声明i的时候,每次执行for循环都会形成一个块级作用域,而在执行输出i的语句时,我们会首先在这个形成的块级作用域查找,从而完成每个作用域中的i保留为0-9的值,所以在输出时能够实现输出0-9

但是如果我们的第一个for循环仍要使用var声明i,那么我们就可以利用闭包来实现输出0-9的功能,代码如下:

 
var arr = []
for (var i = 0; i < 10; i++) {

  (arr[i] = function (j) {
    console.log(j)

  })(i)
}

image.png

在这个过程中我们直接在创建函数的时候,直接对其调用,从而利用闭包的把i此时的值留住,形成闭包,闭包中的内容为arr[i]arr[i]中的内容为function(j){console.log(j)}ji,所以输出0-9

闭包的优点

变量私有化

闭包允许在内部函数中访问外部函数的变量,从而实现变量的私有化。这种机制在框架级别的开发以及一些设计模式中非常有用,可以避免变量被外部随意修改。

 
function counter() {
  let count = 0;

  return function() {
    count++;
    console.log(count);
  };
}

const increment = counter();
increment(); // 输出 1
increment(); // 输出 2

在上面的例子中,count 变量被私有化在 counter 函数内部,外部无法直接访问或修改它。

闭包的缺点

内存泄漏

闭包的一个潜在问题是内存泄漏。由于闭包使得内部函数保持对外部函数作用域中变量的引用,如果这些引用没有被及时释放,可能导致内存占用过高。

 
function createHeavyObject() {
  const heavyObject = /* 创建一个占用大量内存的对象 */;

  return function() {
    console.log(heavyObject);
  };
}

const myClosure = createHeavyObject();
// 此时myClosure包含对createHeavyObject函数作用域中heavyObject的引用

在上述例子中,myClosure 包含对 createHeavyObject 函数作用域中 heavyObject 的引用,即使外部不再需要 heavyObject,它依然无法被垃圾回收。要避免内存泄漏,可以手动解除对不再需要的引用,或者使用一些优化手段。

如何避免内存泄漏

为了避免闭包导致的内存泄漏,可以采取以下措施:

1. 及时解除引用

当不再需要闭包时,手动将对外部作用域变量的引用解除,让垃圾回收机制能够回收相关资源。

 
function createHeavyObject() {
  const heavyObject = /* 创建一个占用大量内存的对象 */;

  return function() {
    console.log(heavyObject);
  };
}

const myClosure = createHeavyObject();
// 手动解除引用
myClosure = null;

2. 使用垃圾回收优化

一些现代 JavaScript 引擎会对闭包进行优化,自动检测不再需要的引用并进行回收。但这并不是一劳永逸的解决方案,仍然建议在代码中注意及时释放不再需要的引用。

结语

闭包是JavaScript中强大而灵活的特性,能够提供变量私有化的能力。然而,要小心使用闭包,以防止潜在的内存泄漏问题。及时释放不再需要的引用是保持代码健康的重要步骤,合理利用闭包将为你的代码带来便利和安全性。

如果有疑问或者错误,欢迎在评论区指出!


原文链接:https://juejin.cn/post/7300779577059131402
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1212023.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Ubuntu·系统·的Linux环境变量配置方法最全】

文章目录 概要读取环境变量的方法小技巧 概要 在Linux环境中&#xff0c;配置环境变量是一种常见的操作&#xff0c;用于指定系统或用户环境中可执行程序的搜索路径。 读取环境变量的方法 在Linux中&#xff0c;可以使用以下两个命令来读取环境变量&#xff1a; export 命令…

Kubernetes(k8s)资源管理

文章目录 Kubernetes资源管理1.资源管理介绍2.YAML语言介绍3.资源管理方式命令式对象管理命令式对象配置声明式对象配置 扩展&#xff1a;配置kubectl命令可以在node节点上运行 Kubernetes资源管理 1.资源管理介绍 在kubernetes中&#xff0c;所有的内容都抽象为资源&#xf…

MongoDB(一):CentOS7离线安装MongoDB单机版与简单使用

CentOS7离线安装MongoDB单机版与简单使用 1、概述2、安装社区版2.1、前置条件2.2、下载.tgz文件2.3、解压文件2.4、安装MongoDB Shell 3、运行MongoDB服务端3.1、关于ulimit3.2、目录设置3.3、创建mongod.conf3.4、运行MongoDB3.5、检查MongoDB是否已运行 4、使用MongoDB4.1、操…

Clear recent project list 清理Idea的最近项目列表

Clear recent project list 清理Idea的最近项目列表 Idea打开过好多项目清理方式mac文件地址Windows文件地址linux 文件地址 Idea打开过好多项目 很多项目都已经从磁盘删除了&#xff0c;但是还在最近的项目中能看到&#xff0c;偶尔点击到&#xff0c;会提示已经不存在。很头…

webstorm基础配置

设置左侧菜单栏文字大小 开启鼠标滚轮控制文字大小 配置自定义注释 设置左侧菜单栏文字大小&#xff1a;file》settings》Appearance&Behavior》Appearance 开启鼠标滚轮控制主界面文字大小&#xff1a;file》settings》Editor》General 配置自定义注释&#xff1a;fi…

【文件包含】phpmyadmin 文件包含(CVE-2014-8959)

1.1漏洞描述 漏洞编号CVE-2014-8959漏洞类型文件包含漏洞等级高危漏洞环境Windows漏洞名称phpmyadmin 文件包含&#xff08;CVE-2014-8959&#xff09; 描述: phpMyAdmin是一套开源的、基于Web的MySQL数据库管理工具。其index.php中存在一处文件包含逻辑&#xff0c;通过二次编…

一键将CSDN博客文章如何转为Markdown

文章目录 1.在CSDN博文页面点击右键&#xff0c;选择“检查”&#xff08;Google浏览器为例&#xff09;。2.在查看器中搜索article_content&#xff0c;找到对应内容&#xff0c;点击…复制为outerHTML。3.打开网址https://tool.lu/markdown/&#xff0c;点击HTML2MD&#xff…

【数据结构】希尔排序(最小增量排序)

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在学习c和算法 ✈️专栏&#xff1a;数据结构 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章有啥瑕疵 希望大佬指点一二 如果文章对你有帮助…

【C++面向对象】11. 数据抽象*

文章目录 【 1. 访问标签强制抽象 】【 2. 设计策略 】 数据抽象 是指只向外界提供关键信息&#xff0c;并隐藏其后台的实现细节&#xff0c;即只表现必要的信息而不呈现细节。数据抽象是一种依赖于接口和实现分离的编程&#xff08;设计&#xff09;技术。数据抽象的好处&…

【面试经典150 | 位运算】数字范围按位与

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;公共前缀方法二&#xff1a;n & (n-1) 写在最后 Tag 【位运算】 题目来源 201. 数字范围按位与 题目解读 计算给定区间内所有整数的按位与的结果。 解题思路 本题朴素的方法是直接将区间内的所有整数按位与&…

FindMy技术定位身份证

身份证是我们日常生活中不可缺少的重要证件。无论是购买房产、车辆&#xff0c;还是乘坐飞机、火车、汽车等交通工具&#xff0c;甚至是办理银行业务等&#xff0c;都需要提供身份证原件。因此&#xff0c;身份证对于我们来说&#xff0c;其重要性不言而喻&#xff0c;一旦丢失…

Python如何使用Pyecharts+TextRank生成词云图?

Python如何使用PyechartsTextRank生成词云图&#xff1f; 1 应用场景2 关于Pyecharts2.1 Pyecharts简介2.2 Pyecharts安装2.3 Pyecharts支持的图形2.4 Pyecharts的一个示例 3 关于TextRank3.1 TextRank简介3.2 TextRank安装 4 词云图的生成过程4.1 导入需要的包4.2 目标文件4.3…

2023.11.16 hivesql之条件函数,case when then

目录 一.Conditional Functions条件函数 二.空值相关函数 三&#xff1a;使用注意事项 3.1 then后面不能接子查询 3.2 then后面只能是结果值 3.3 then后面能不能接两列 四.用于建表新增字段使用场景 一.Conditional Functions条件函数 -- 演示条件函数 -- if(条件判断,t…

【软考篇】中级软件设计师 第三部分(一)

中级软件设计师 第三部分&#xff08;一&#xff09; 十七. I/O管理软件十八. 输入/输出技术十九. 总线系统二十. 磁盘管理20.1 移臂调度算法20.2 缓冲区 二十一. 操作系统二十二. 数据库22.1 三级模式-两级映射22.2 数据库设计22.3 规范化理论22.4 范式判断22.5 模式分解 十七…

探索arkui(1)--- 布局(线性/层叠/弹性)

前端开发布局是指前端开发人员宣布他们开发的新网站或应用程序正式上线的活动。在前端开发布局中&#xff0c;开发人员通常会展示新网站或应用程序的设计、功能和用户体验&#xff0c;并向公众宣传新产品的特点和优势。前端开发布局通常是前端开发领域的重要事件&#xff0c;吸…

如何用SaleSmartly集成WhatsApp账号(内含WhatsApp个人号、商业号、API号对比图)

用SaleSmartly集成WhatsApp账号 如果企业有多个WhatsApp账号&#xff0c;无论是个人账号还是工作账号&#xff0c;员工操作起来可能会觉得难以管理和切换。SaleSmartly就可以解决这个问题&#xff0c;让员工在一个平台上同时使用多个WhatsApp账号&#xff0c;不需要频繁地登录和…

C++初阶,详解类和对象(2)

详解类和对象&#xff08;2&#xff09; 一&#xff0c;前言二&#xff0c;构造函数2.1构造函数概念2.2构造函数特性 三&#xff0c;析构函数3.1析构函数概念3.2析构函数特性 一&#xff0c;前言 上一篇我们讲了类的大体框架&#xff0c;这篇内容我们要重点来说一说类的几个默…

iis特殊字符的轉義問題解決

今天發現有個問題&#xff0c;部分圖片的靜態資源帶有號&#xff0c;導致無法直接在瀏覽器獲取圖片&#xff0c;百度了一下&#xff0c;修改了注冊表&#xff0c;發現沒什麽軟用&#xff0c;最後找到一篇博客&#xff0c;解決了 解決

k8s的error: metrics not available yet问题处理

kubectl top node报错处理 解决步骤环境说明问题现象初次排查问题解决版本兼容性metric-server.yaml 问题验证 解决步骤 因项目要求&#xff0c;需在k8s集群中使用 kubectl top node命令&#xff0c;但是一直报error: metrics not available yet错误。为了更好的复现问题&…

【知识增强】A Survey of Knowledge-Enhanced Pre-trained LM 论文笔记

A Survey of Knowledge-Enhanced Pre-trained Language Models Linmei Hu, Zeyi Liu, Ziwang Zhao, Lei Hou, Liqiang Nie, Senior Member, IEEE and Juanzi Li 2023年8月的一篇关于知识增强预训练模型的文献综述 论文思维导图 思维导图网页上看不清的话&#xff0c;可以存…