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

前言

在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
 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Shiro快速入门之二

一、前言 Shiro快速入门之一 介绍了Shiro三大核心组件&#xff0c;四大核心功能&#xff0c;以及一个简单的Test Demo&#xff0c;接下来两篇我会用一个比较完整的例子来讲述Shiro的认证及授权是怎么做的&#xff0c;本篇侧重于介绍认证的过程 二、Shiro认证例子 1、例子概述…

H5三网魔幻手游【众神传奇】win服务端+GM授权后台+架设教程

搭建资源下载地址&#xff1a;H5三网魔幻手游【众神传奇】win服务端GM授权后台架设教程-海盗空间

锂价疲软,市场需求持续低迷,赣锋锂业在短期内将继续面临痛苦

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 公司介绍 在赣锋锂业(01772)(002460)网站上&#xff0c;赣锋锂业称自己是一家拥有“中国第一”和“世界最大锂金属生产商”的“锂化合物产能”公司。 根据其2022财年年度报告&#xff0c;该公司最近一个财年的收入有84%和1…

日历应用程序 BusyCal mac中文版软件特点

BusyCal mac是一款日历应用程序&#xff0c;它可以帮助用户轻松地管理日程安排、事件提醒、会议安排等。BusyCal 支持 macOS 和 iOS 平台&#xff0c;并且可以与 iCloud、Google 日历、Exchange 等多种日历服务进行同步。 BusyCal mac软件特点 强大的日历功能&#xff1a;Busy…

Rust编程中的共享状态并发执行

1.共享状态并发 虽然消息传递是一个很好的处理并发的方式&#xff0c;但并不是唯一一个。另一种方式是让多个线程拥有相同的共享数据。在学习Go语言编程过程中大家应该听到过一句口号:"不要通过共享内存来通讯"。 在某种程度上&#xff0c;任何编程语言中的信道都类…

活动通知邀请函H5页面制作源码系统+动感的背景音乐 自定义你想要的页面 源码完全开源可二开 带完整搭建教程

在现代社交活动中&#xff0c;一份精美、个性化的活动邀请函不仅能够展现主办方的品味和诚意&#xff0c;还可以吸引更多的参与者。然而&#xff0c;制作一份精美的活动邀请函需要专业的设计和技术支持&#xff0c;这对于很多非专业人士来说是一个难题。此外&#xff0c;传统的…

mysql之主从复制和读写分离

一、主从复制 1、定义 主mysql上的数据&#xff08;新增或修改库、表里的数据&#xff09;都会同步到从mysql上 2、mysql的主从复制模式&#xff08;面试题&#xff09; &#xff08;1&#xff09;异步复制&#xff08;常用&#xff09;&#xff1a;默认的复制模式。客户端…

网络运维Day15

文章目录 Prometheus简介环境准备配置模板机环境部署阿里镜像源实验环境准备 部署prometheus服务查看及测试 Promethues 被监控端Grafana简介部署 Grafana 服务器修改数据源 监控数据库安装部署Mariadb安装导出器修改 Prometheus服务端配置Grafana配置 总结 Prometheus简介 Pr…

需要买哪些网络设备才能过等保?求解!

随着等保2.0的落地执行&#xff0c;越来越多的企业需要过等保。但不少企业都是第一次过等保&#xff0c;对于等保政策不是很了解&#xff0c;有小伙伴问&#xff0c;需要买哪些网络设备才能过等保&#xff1f;这里我们小编就给大家来简单回答一下&#xff0c;仅供参考哈&#x…

Q learning

Q learning Q Learning是强化学习算法中的一个经典算法。在一个决策过程中&#xff0c;我们不知道完整的计算模型&#xff0c;所以需要我们去不停的尝试。 算法流程 整体流程如下&#xff1a; Q-table 初始化 第一步是创建 Q-table&#xff0c;作为跟踪每个状态下的每个动作…

电商平台api接口对接电商数据平台,获取商品详情页面实时信息须知

随着互联网的发展和普及&#xff0c;电商平台已成为人们日常生活中不可或缺的一部分。而为了保证电商平台的正常运行&#xff0c;平台与开发者之间需要进行数据交互&#xff0c;这便涉及到了电商平台API接口对接的问题。本文将详细介绍电商平台API接口对接的须知事项。 一、了解…

双算法SSL证书

国际算法的优势与挑战 1. RSA算法 RSA算法是一种基于大素数分解的非对称加密算法&#xff0c;长期以来一直是SSL证书的主流选择之一。然而&#xff0c;随着计算能力的提高&#xff0c;RSA算法的密钥长度需要不断增加&#xff0c;以维持足够的安全性。 2. ECC算法 椭圆曲线密…

迅软DSE答疑专业解析:内网遭受攻击的威胁到底有多大

当今数字化时代&#xff0c;企业数据安全已演变为企业生存和发展的至关重要因素。随着信息技术的迅猛发展&#xff0c;企业内网不仅是承载核心数据和信息的关键平台&#xff0c;也成为黑客和恶意软件攻击的主要目标。因此&#xff0c;确保企业数据安全和内网安全已成为企业管理…

ADC内部电源监控

文章目录 前言一、分析芯片手册1、43.5 ADC internal supply monitoring2、11.1.1.1 Chip Control register (CHIPCTL)1&#xff09;SIM_CHIPCTL[ADC_SUPPLY]2&#xff09;SIM_CHIPCTL[ADC_SUPPLYEN] 3、44.4.2 ADC Status and Control Register 1 (SC1A - aSC1P) 二、EB配置1、…

@CacheInvalidate(name = “xxx“, key = “#results.![a+b]“,multi = true)是什么意思

@CacheInvalidate 注解是 JetCache 框架提供的注解,它是由阿里巴巴开源的组织 Alibaba Group 开发和维护的。JetCache 是一款基于注解的缓存框架,提供了丰富的缓存功能和灵活的配置选项,可用于增强应用程序的性能和可扩展性。JetCache 支持多种缓存后端,包括内存缓存、Redi…

Ubuntu22.04源码安装ROS-noetic(ROS1非ROS2),编译运行VINS-MONO

1. Ubuntu22.04源码编译安装ROS-noetic 由于22.04默认安装ROS2&#xff0c;但很多仓库都是基于ROS1的&#xff0c;不想重装系统&#xff0c;参考这两个博客安装了ROS-noetic&#xff1a; 博客1. https://blog.csdn.net/Drknown/article/details/128701624博客2. https://zhua…

(七)Spring源码解析:Spring事务

对于事务来说&#xff0c;是我们平时在基于业务逻辑编码过程中不可或缺的一部分&#xff0c;它对于保证业务及数据逻辑原子性立下了汗马功劳。那么&#xff0c;我们基于Spring的声明式事务&#xff0c;可以方便我们对事务逻辑代码进行编写&#xff0c;那么在开篇的第一部分&…

CRM系统对科技企业有哪些帮助

随着国家政策的倾斜和5G等相关基础技术的发展&#xff0c;中国人工智能产业在各方的共同推动下进入爆发式增长阶段&#xff0c;市场发展潜力巨大。CRM客户管理系统作为当下最热门的企业应用&#xff0c;同样市场前景广阔。那么&#xff0c;CRM系统对科技企业有哪些帮助&#xf…

Python开源项目VQFR——人脸重建(Face Restoration),模糊清晰、划痕修复及黑白上色的实践

Python Anaconda 的安装等请参阅&#xff1a; Python开源项目CodeFormer——人脸重建&#xff08;Face Restoration&#xff09;&#xff0c;模糊清晰、划痕修复及黑白上色的实践https://blog.csdn.net/beijinghorn/article/details/134334021 VQFR也是 腾讯 LAB 的作品&…

ninja 编译介绍

首先献上官方链接 https://ninja-build.org/manual.html Ninja 何以存在&#xff1f; 从官方文档看出&#xff0c;编译很快&#xff01;怎么实现的呢&#xff1f;&#xff1f;所以&#xff0c;还是值得了解一下的&#xff0c;继续~ 编译优势 项目中使用 具体语法&#xff0c;…