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

news2024/12/12 5:39:32

在这里插入图片描述

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

文章目录

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


在这里插入图片描述


💯前言

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

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


垃圾回收的核心原理

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


核心过程

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

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

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

函数作用域与垃圾回收

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

function a() {
  var x = 1; // 局部变量 x
  x++;      // 修改 x
  console.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; // 局部变量 x
  return function () {
    x++; // 操作外部变量 x
    return 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.coloradmin.cn/o/2258063.html

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

相关文章

android studio 读写文件操作(应用场景三)

android studio版本:2023.3.1 patch2 例程:filesaveandread 其实我写这个都是我记录我要做后个数独小游戏,每一个都是为了解决一个问题。即是分享也是备忘,反正我什么都不会,就是一顿瞎改,不行就研究。这…

分库分表基本概念讲解

一、基本概念 产生背景 在数据爆炸的年代,单表数据达到千万级别,甚至过亿的量,都是很常见的情景。这时候再对数据库进行操作就是非常吃力的事情了,select个半天都出不来数据,这时候业务已经难以维系。不得已&#xf…

华为自反ACL实验

一、实验背景 做这个实验的原因是最近公司里上了三台小程序服务器,由于三台服务器的端口都映射出去了,领导要求A网段的三台服务器不能访问内网B,C网段,同时B、C网段内网用户可以访问A段的94、95、96服务器; 也就是PC4\…

美图撕掉蔡文胜标签

卖掉比特币的美图不投机了。 作者|周立青 编辑|杨舟 12月5日,比特币突破10万美元大关,曾花费1亿美元购入虚拟货币的美图宣布已出售所有加密货币。 美图在港交所发布公告称,自2024年11月起,公司已开始出售其持有的加密货币&…

git拉取代码报错问题:Pulling is not possible because you have unmerged files. hint

我们在工作中&#xff0c;需要切换到另外一个分支&#xff0c;拉取代码的时候会报这样的问题&#xff1a; Pulling is not possible because you have unmerged files. hint: Fix them up in the work tree, and then use git add/rm <file> hint: as appropriate to ma…

青训营-豆包MarsCode技术训练营试题解析三十七

引言 随着AI领域的发展&#xff0c;底层算法确实起到了决定性的作用。为了跟上这个快速发展的领域&#xff0c;我们需要不断学习和提升自己的技能。刷题是一种很好的方式&#xff0c;可以帮助我们巩固基础知识&#xff0c;提高解决问题的能力。 介绍 ‌豆包青训营‌是由字节…

openlayers地图缓存添加

//通过安装包localforage&#xff08;npm install localforage&#xff09;或https://cdnjs.cloudflare.com/ajax/libs/localforage/1.10.0/localforage.min.js tileCacheStore.js import localforage from localforage var tileCacheStorenull;// 从缓存中获取该瓦片 functio…

《IP 电话:选型指南与应用优势》

《IP 电话&#xff1a;选型指南与应用优势》 一、IP 电话概述二、IP 电话的选型1. 功能与应用2. 性能及可靠性3. 兼容及可升级性4. 外观设计及管理需求5. 性价比 三、IP 电话的应用场景1. 企业办公2. 工厂和仓库3. 酒店和旅游业4. 医疗机构5. 零售业6. 教育机构7. 个人用户 四、…

解决Logitech G hub 无法进入一直转圈的方案(2024.12)

如果你不是最新版本无法加载尝试以下方案&#xff1a;删除AppData 文件夹下的logihub文件夹 具体路径&#xff1a;用户名根据实际你的请情况修改 C:\Users\Administrator\AppData\Local 如果你有通过lua编译脚本&#xff0c;记得备份&#xff01;&#xff01; ↓如果你是最新…

[网络爬虫] Jsoup : HTML 解析工具

1 概述 简介 Jsoup是一款基于Java的HTML解析器&#xff0c;它提供了一种简单、灵活且易于使用的API&#xff0c;用于从URL、文件或字符串中解析HTML文档。它可以帮助开发人员从HTML文档中提取数据、操作DOM元素、处理表单提交等。 主要特点 Jsoup的主要特点包括&#xff1a; 简…

top命令和系统负载

1 top中的字段说明 top是一个实时系统监视工具&#xff0c;可以动态展现出 CPU 使用率、内存使用情况、进程状态等信息&#xff0c;注意这些显示的文本不能直接使用 > 追加到文件中。 [rootvv~]# top -bn 1 | head top - 20:08:28 up 138 days, 10:29, 4 users, load av…

golang实现简单的redis服务4(实现过期时间功能)

为什么要做过期时间?redis失效时间是如何做的redis有那些过期策略,优缺点,实现原理?redis使用的什么方案 redis 有那些内存淘汰策略?常用的是什么,为什么?noeviction: 不处理lru: 未使用时间最久的keylfu: 使用次数最少的keyrandom: 随机keyvolatile与allkeys的区别 仓库地…

VMware:如何在CentOS7上开启22端口

打开虚拟机&#xff1a;【编辑】【虚拟机网络设置】 其中填入的虚拟机IP地址是虚拟机中centos的IP地址&#xff0c;虚拟机端口为需要映射的centos端口 配置好之后保存&#xff0c;打开宿主机 win cmd telnet 192.168.1.26 22 如果出现上述窗口&#xff0c;则说明已经成功开放…

项目中使用AntV L7地图(五)添加飞线

项目中使用AntV L7地图&#xff0c;添加 飞线 文档地址&#xff1a;https://l7.antv.antgroup.com/zh/examples/line/animate/#trip_animate 一、初始化地图 使用的地图文件为四川地图JSON&#xff0c;下载地址:https://datav.aliyun.com/portal/school/atlas/area_selector#&…

基于FPGA的智能电子密码指纹锁(开源全免)

基于FPGA的智能电子密码指纹锁 一、功能描述硬件资源需求 二、整体框架知识准备AS608指纹模块4*4数字键盘模块 三、Verilog代码实现以及仿真验证1.AS608_data模块2.check_hand模块3.four_four_key模块4.check_mima模块5.change_mima模块6.seg_ctrl模块7.uart_top模块8.key_debo…

【Vue2+Element-ui】el-dialog宽度适配

1、不适配问题 分辨率100%-页面 分辨率150%-页面 在项目中&#xff0c;我开发分辨率一直是100%&#xff0c;但是客户使用的分辨率不相同&#xff0c;所以宽度要适配 2、解决-封装mixins.js 1)、封装的mixins 我将宽度设置成动态的&#xff0c;因为我的项目中需求不同。 expor…

css矩形样式,两边圆形

废话不多说&#xff0c;代码如下&#xff0c;直接拷贝即可使用&#xff1a; index.vue文件 <template><view class"wrap"><view class"tabs"><view class"tab active"><view class"name">标签</view…

【OpenCV】图像阈值

简单阈值法 此方法是直截了当的。如果像素值大于阈值&#xff0c;则会被赋为一个值&#xff08;可能为白色&#xff09;&#xff0c;否则会赋为另一个值&#xff08;可能为黑色&#xff09;。使用的函数是 cv.threshold。第一个参数是源图像&#xff0c;它应该是灰度图像。第二…

使用微信小程序调用飞桨PaddleX平台自行训练的模型——微信小程序用训练的牡丹花模型Demo测试

&#x1f3bc;个人主页&#xff1a;【Y小夜】 &#x1f60e;作者简介&#xff1a;一位双非学校的大二学生&#xff0c;编程爱好者&#xff0c; 专注于基础和实战分享&#xff0c;欢迎私信咨询&#xff01; &#x1f386;入门专栏&#xff1a;&#x1f387;【MySQL&#xff0…

Wordpress设置固定链接形式后出现404错误

比如固定连接设置为 /archives/%post_id%.html 这种形式&#xff0c;看起来比较舒服。对搜索引擎也友好。 出现404需要设置伪静态