【前端】JavaScript中的闭包与垃圾回收机制详解


在这里插入图片描述

博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳]
本文专栏: 前端

文章目录

  • 💯前言
  • 💯垃圾回收机制(Garbage Collection, GC)
    • 垃圾回收的核心原理
      • 核心过程
    • 函数作用域与垃圾回收
      • 运行分析
      • 输出结果
    • 垃圾回收的局限性与挑战
  • 💯闭包(Closure)
    • 闭包的概念
      • 闭包的特点
    • 闭包的运行机制
      • 运行分析
      • 输出结果
    • 闭包的内存管理
      • 内存泄漏的预防
  • 💯闭包与垃圾回收的结合
    • 未使用闭包的情况
      • 输出结果
    • 使用闭包的情况
  • 💯闭包的高级应用
    • 场景1:私有变量封装
    • 场景2:防抖与节流
    • 场景3:函数式编程中的应用
  • 💯小结


在这里插入图片描述


💯前言

  • JavaScript 编程范畴内,垃圾回收机制(GC)闭包是两个高度复杂且具关键意义的概念。这些机制直接影响内存管理状态维护,构成了现代 JavaScript 编程的重要理论基础。为了帮助深入理解这些机制的核心原理和实际应用,本文将通过系统化的理论阐述和详细的代码示例展示它们在不同场景中的表现及潜在挑战。
    JavaScript在这里插入图片描述

💯垃圾回收机制(Garbage Collection, GC)


垃圾回收的核心原理

JavaScript采用自动内存管理,通过垃圾回收机制检测并回收不再被引用的变量或对象,以防止内存泄漏并优化性能。垃圾回收机制是动态语言中的关键特性,确保了开发者无需手动管理内存分配与释放。


核心过程

  1. 内存分配:当程序创建变量或对象时,JavaScript会为其分配内存。
  2. 内存回收:垃圾回收机制会释放那些不再被引用的变量或对象所占用的内存。

垃圾回收器通过可达性(Reachability)的概念判断对象是否需要回收:

  • 如果一个对象可以通过引用链从根对象(如全局对象)访问到,则该对象被视为可达,不会被回收。
  • 如果一个对象无法通过任何引用链访问到,则该对象会被标记为不可达,随后被垃圾回收。

函数作用域与垃圾回收

以下代码展示了垃圾回收机制在函数中的典型表现:

function a() {var x = 1; // 局部变量 xx++;      // 修改 xconsole.log(x); // 输出 x 的值
}a(); // 第一次调用
a(); // 第二次调用

在这里插入图片描述


运行分析

  1. 函数未执行时:

    • a() 函数的定义存储在内存中,其内部内容尚未被解析。
  2. 函数执行时:

    • 局部变量 x 被分配内存,并初始化为 1
    • 执行 x++ 后,变量值变为 2,并输出。
  3. 函数执行结束后:

    • 函数作用域结束,局部变量 x 不再被引用,垃圾回收机制释放其内存。

输出结果

无论调用 a() 多少次,输出结果始终为 2,因为每次调用都重新初始化了变量 x


垃圾回收的局限性与挑战

尽管垃圾回收机制能够自动释放未使用的内存,但其运行效率可能受复杂引用关系影响,特别是在以下场景中:

  • 循环引用:两个对象相互引用,可能导致内存无法回收。
  • 全局变量:全局变量的生命周期与程序运行周期一致,长期未释放可能引发内存泄漏。

💯闭包(Closure)


闭包的概念

闭包是指函数能够“记住”并访问其定义时的词法作用域,即使该函数在其定义作用域之外被调用。闭包是JavaScript的核心特性之一,为函数式编程提供了重要支持。


闭包的特点

  1. 闭包能够持有对其外部函数作用域变量的引用。
  2. 变量的生命周期因闭包的存在而被延长,不会在外部函数执行结束后立即被垃圾回收。
  3. 闭包可以作为数据封装和状态保持的工具。

闭包的运行机制

以下代码展示了闭包的基本实现:

function a() {var x = 1; // 局部变量 xreturn function () {x++; // 操作外部变量 xreturn x; // 返回变量值};
}var w = a(); // 保存闭包函数
console.log(w()); // 输出 2
console.log(w()); // 输出 3
console.log(w()); // 输出 4

在这里插入图片描述


运行分析

  1. 调用 a() 时,函数 a 的作用域内创建了局部变量 x,并将其初始化为 1
  2. a() 返回一个匿名函数,此函数引用了外部变量 x,形成闭包。
  3. 变量 w 保存了闭包函数,因此外部变量 x 不会被垃圾回收。
  4. 每次调用 w() 时,闭包都会操作同一个变量 x,实现状态的持久化。

输出结果

2
3
4


闭包的内存管理

闭包引用的变量在外部函数执行结束后不会被垃圾回收,除非闭包本身不再被引用。这一特性使闭包能够维护状态,但若不加以控制,可能会引发内存泄漏。


内存泄漏的预防

开发者可以通过以下方式避免闭包引发的内存泄漏:

  1. 避免不必要的闭包:在代码设计中,应减少对闭包的滥用。
  2. 手动解除引用:在合适的时机将不再使用的闭包变量设为 null,以允许垃圾回收器回收。

💯闭包与垃圾回收的结合


未使用闭包的情况

以下代码展示了变量未被闭包引用时的表现:

function a() {var x = 1;x++;return x;
}console.log(a()); // 输出 2
console.log(a()); // 输出 2

在这里插入图片描述

每次调用 a() 都会重新初始化变量 x,因为函数执行结束后,局部变量已被垃圾回收。


输出结果

2
2

使用闭包的情况

function a() {var x = 1;return function () {x++;return x;};
}var w = a();
console.log(w()); // 输出 2
console.log(w()); // 输出 3

在这里插入图片描述

由于 w 持有闭包函数,x 的生命周期被延长,每次调用都会累加 x 的值。


💯闭包的高级应用


场景1:私有变量封装

闭包可用于模拟私有变量,保护数据不被外部直接访问:

function createCounter() {let count = 0;return {increment: function () {count++;return count;},decrement: function () {count--;return count;}};
}const counter = createCounter();
console.log(counter.increment()); // 输出 1
console.log(counter.increment()); // 输出 2
console.log(counter.decrement()); // 输出 1

场景2:防抖与节流

闭包是实现防抖与节流功能的核心:

function debounce(fn, delay) {let timer = null;return function (...args) {clearTimeout(timer);timer = setTimeout(() => {fn.apply(this, args);}, delay);};
}const log = debounce(() => console.log('Debounced!'), 300);
log(); // 等待 300ms 后输出 "Debounced!"

场景3:函数式编程中的应用

在函数式编程范式中,闭包被广泛用于高阶函数的实现:

function multiplier(factor) {return function (number) {return number * factor;};
}const doubler = multiplier(2);
console.log(doubler(5)); // 输出 10
const tripler = multiplier(3);
console.log(tripler(5)); // 输出 15

💯小结

  • 在这里插入图片描述
  1. 垃圾回收机制通过释放无用变量来优化内存,但闭包引用的变量会延长生命周期。
  2. 闭包状态持久化提供了便利,可实现数据封装功能扩展,但需谨慎使用以避免内存泄漏。
  3. 实践建议:开发者应合理设计闭包,确保状态管理的同时控制内存使用。

在这里插入图片描述


在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

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

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

相关文章

单臂路由配置

知识点 单臂路由指在路由器上的一个接口配置子接口(逻辑接口)来实现不同vlan间通信 路由器上的每个物理接口都可以配置多个子接口(逻辑接口) 公司的财务部、技术部和业务部有多台计算机,它们使用一台二层交换机进行互…

verilog编程规范

verilog编程规范 文章目录 verilog编程规范前言一、代码划分二、verilog编码ABCDEFG 前言 高内聚,低耦合,干净清爽的代码 一、代码划分 高内聚: 一个功能一个模块干净的接口提取公共的代码 低耦合: 模块之间低耦合尽量用少量…

WEB安全基础知识

WAF全称为Web Application Firewall(网页应用防火墙)是一种专门设计用来保护web应用免受各种网络攻击的安全防护措施。它位于客户端与服务器之间,监控和过滤HTTP流量,从而拦截恶意请求、识别并防御常见的web攻击。 WAF的主要功能…

qemu安装arm64架构银河麒麟

qemu虚拟化软件,可以在一个平台上模拟另一个硬件平台,可以支持多种处理器架构。 一、安装 安装教程:https://blog.csdn.net/qq_36035382/article/details/125308044 下载链接:https://qemu.weilnetz.de/w64/2024/ 我下载的是 …

前端怎么用 EventSource?EventSource 怎么配置请求头及加参数?EventSourcePolyfill 使用方法

前言 在前端开发中,特别是实时数据更新的场景下,EventSource 是一个非常实用的 API。它允许浏览器与服务器建立单向连接,服务器可以持续地发送数据给客户端,而无需客户端不断轮询。本文将详细介绍 EventSource 的使用方法、如何配…

188-下翻便携式6U CPCI工控机箱

一、板卡概述 下翻式CPCI便携工控机,系统采用6u cpci背板结构,1个系统槽,7个扩展槽, 满足对携带的需求,可装标准6U8槽CPCI主板,8个扩展槽, 满足客户对空间扩展的需求.可宽温服务的工作产品,15高亮度液晶显示屏,超薄88键笔记本键盘,触摸式鼠标,加固型机箱结构,使它能够适应各种复…

网页核心页面设计(第9章)

一、多个边框阴影 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-…

SpringBoot中Selenium详解

文章目录 SpringBoot中Selenium详解一、引言二、集成Selenium1、环境准备1.1、添加依赖 2、编写测试代码2.1、测试主类2.2、页面对象2.3、搜索组件 三、使用示例四、总结 SpringBoot中Selenium详解 一、引言 在现代软件开发中&#xff0c;自动化测试是提高软件质量、减少重复…

Edge SCDN的独特优势有哪些?

强大的边缘计算能力 Edge SCDN&#xff08;边缘安全加速&#xff09;是酷盾安全推出的边缘集分布式 DDoS 防护、CC 防护、WAF 防护、BOT 行为分析为一体的安全加速解决方案。通过边缘缓存技术&#xff0c;智能调度使用户就近获取所需内容&#xff0c;为用户提供稳定快速的访问…

Fastapi教程:使用aioredis异步访问redis

本文将介绍如何使用 FastAPI 异步访问 Redis&#xff0c;包括环境配置、连接创建、数据库初始化、增删查改操作、键过期、管道&#xff08;pipeline&#xff09;操作以及事务管理等内容。 环境准备 首先&#xff0c;我们需要安装必要的依赖包。Redis 是一个键值存储系统&…

duxapp 2024-12-09 更新 PullView可以弹出到中间,优化CLI使用体验

UI库 修复 Button 禁用状态失效的问题Modal 组件即将停用&#xff0c;请使用 PullView 基础库 PullView side 新增 center 指定弹出到屏幕中间PullView 新增 duration 属性&#xff0c;指定动画时长新增 useBackHandler hook 用来阻止安卓端点击返回键 RN端 修复 windows …

多线程与线程互斥

目录 引言 一、多线程设计 多线程模拟抢票 二、互斥锁 互斥量的接口 修改抢票代码 锁的原理 锁的封装&#xff1a;RAII 引言 随着信息技术的飞速发展&#xff0c;计算机软件正变得越来越复杂&#xff0c;对性能和响应速度的要求也日益提高。在这样的背景下&#xff0c;…

Vue导出报表功能【动态表头+动态列】

安装依赖包 npm install -S file-saver npm install -S xlsx npm install -D script-loader创建export-excel.vue组件 代码内容如下&#xff08;以element-ui样式代码示例&#xff09;&#xff1a; <template><el-button type"primary" click"Expor…

ZUC256 Go Go Go!!!

文章目录 背景运行效果代码 背景 因业务需要使用ZUC算法&#xff0c;GitHub上又没有对ZUC256相对应的Go语言的实现。 吃水不忘挖井人&#xff0c;在这里感谢GmSSL及BouncyCastle两个强大的密码学库&#xff01; 本ZUC256的编写&#xff0c;参考了这两个库及中科院软件院发布的…

力扣打卡12:复原IP地址

链接&#xff1a;93. 复原 IP 地址 - 力扣&#xff08;LeetCode&#xff09; 这道题需要对字符串进行操作&#xff0c;我选择了三层循环&#xff0c;其实还可以递归。 我在循环时进行了剪枝&#xff0c;比如一些情况直接跳出循环。 我的代码&#xff1a; class Solution { p…

The ‘.git/hooks/pre-push‘ hook was ignored because it‘s not set as executable.

Mac上使用Git提交代码提示&#xff1a; hint: The .git/hooks/prepare-commit-msg hook was ignored because its not set as executable. hint: You can disable this warning with git config advice.ignoredHook false. hint: The .git/hooks/commit-msg hook was ignored b…

【实践·专业课】内存管理-存储管理-文件系统

1. 基于Linux的简单区块链实现 1.1. 环境准备 确保使用的 Linux 系统&#xff08;如 Ubuntu、CentOS 等&#xff09;已安装 Python 3。 在终端输入python3命令&#xff0c;若出现 Python 解释器的版本信息等提示&#xff0c;则表示已安装&#xff1b; 若提示未找到命令&…

MySQL 学习 之 批量插入数据性能问题

文章目录 现象优化 现象 在使用 kettle 同步大数据的数据到我们的 MySQL 数据库中时发现&#xff0c;数据量大时插入效率很慢&#xff0c;大约在 2000/s 优化 在 MySQL 驱动连接中添加 rewriteBatchedStatementstrue 参数&#xff0c;减少 网络 IO DB IO 耗时 默认关闭指定…

2个GitHub上最近比较火的Java开源项目

1. SpringBlade 微服务架构 标题 SpringBlade 微服务架构 摘要 SpringBlade 是一个由商业级项目升级优化而来的微服务架构&#xff0c;采用Spring Boot 3.2、Spring Cloud 2023等核心技术构建&#xff0c;遵循阿里巴巴编码规范&#xff0c;提供基于React和Vue的两个前端框架&am…

MongoDB 建模调优change stream实战

MongoDB开发规范 &#xff08;1&#xff09;命名原则。数据库、集合命名需要简单易懂&#xff0c;数据库名使用小写字符&#xff0c;集合名称使用统一命名风格&#xff0c;可以统一大小写或使用驼峰式命名。数据库名和集合名称均不能超过64个字符。 &#xff08;2&#xff09…