2024-09-06 深入JavaScript高级语法十六——JS的内存管理和闭包

news2024/10/1 14:48:14

目录

  • 1、JS内存管理
    • 1.1、认识内存管理
    • 1.2、JS的内存管理
    • 1.3、JS的垃圾回收
      • 1.3.1、常见的 GC 算法 - 引用计数
      • 1.3.2、常见的 GC 算法﹣标记清除
  • 2、JS闭包
    • 2.1、JS中函数是一等公民
    • 2.2、JS中闭包的定义
    • 2.3、闭包的访问过程
    • 2.4、闭包的内存泄漏
    • 2.5、JS闭包内存泄漏案例
    • 2.6、AO不使用的属性

1、JS内存管理

1.1、认识内存管理

  • 不管什么样的编程语言,在代码的执行过程中都是需要给它分配内存的,不同的是某些编程语言需要我们自己手动的管理内存,某些编程语言会可以自动帮助我们管理内存:
  • 不管以什么样的方式来管理内存,内存的管理都会有如下的生命周期:
  • 第一步:分配申请你需要的内存(申请);
  • 第二步:使用分配的内存(存放一些东西,比如对象等);
  • 第三步:不需要使用时,对其进行释放;
  • 不同的编程语言对于第一步和第三步会有不同的实现:
  • 手动管理内存:比如 C 、 C ++,包括早期的 OC ,都是需要手动来管理内存的申请和释放的( malloc 和 free 函数);
  • 自动管理内存:比如 Java 、 JavaScript 、 Python 、 Swift 、 Dart 等,它们有自动帮助我们管理内存;
  • 我们可以知道 JavaScript 通常情况下是不需要手动来管理的。

1.2、JS的内存管理

  • JavaScript 会在定义变量时为我们分配内存。
  • 但是内存分配式是一样的吗?
  • JS 对于基本数据类型内存的分配会在执行时,直接在栈空间进行分配;
  • JS 对于复杂数据类型内存的分配会在堆内存中开辟一块空间,并且会将这块空间的指针返回值通过变量进行引用
    在这里插入图片描述

1.3、JS的垃圾回收

  • 因为内存的大小是有限的,所以当内存不再需要的时候,我们需要对其进行释放,以便腾出更多的内存空间。
  • 在手动管理内存的语言中,我们需要通过一些方式自己来释放不再需要的内存,比如 free 函数:
  • 但是这种管理的方式其实非常的低效,影响我们编写逻辑代码的效率;
  • 并且这种方式对开发者的要求也很高,并且一不小心就会产生内存泄露;
  • 所以大部分现代的编程语言都是有自己的垃圾回收机制:
  • 垃圾回收的英文是 Garbage Collection ,简称 GC ;
  • 对于那些不再使用的对象,我们都称之为是垃圾,它需要被回收,以释放更多的内存空间;
  • 而我们的语言运行环境,比如 Java 的运行环境 JVM , JavaScript 的运行环境 js 引擎都有内存垃圾回收器;
  • 垃圾回收器我们也会简称为 GC ,所以在很多地方你看到 GC 其实指的是垃圾回收器;
  • 但是这里又出现了另外一个很关键的问题: GC 怎么知道哪些对象是不再使用的呢?
  • 这里就要用到 GC 的算法了

1.3.1、常见的 GC 算法 - 引用计数

  • 引用计数:
  • 当一个对象有一个引用指向它时,那么这个对象的引用就+1,当一个对象的引用为0时,这个对象就可以被销毁掉;
  • 这个算法有一个很大的弊端就是会产生循环引用;

在这里插入图片描述

1.3.2、常见的 GC 算法﹣标记清除

  • 标记清除:
  • 这个算法是设置一个根对象( root object ),垃圾回收器会定期从这个根开始,找所有从根开始有引用到的对象,对于那些没有引用到的对象,就认为是不可用的对象;
  • 这个算法可以很好的解决循环引用的问题;
    在这里插入图片描述

2、JS闭包

2.1、JS中函数是一等公民

  • 在 JavaScript 中,函数是非常重要的,并且是一等公民:
  • 那么就意味着函数的使用是非常灵活的;
  • 函数可以作为另外一个函数的参数,也可以作为另外一个函数的返回值来使用(即可称之为:高阶函数);
  • 自己编写一个高阶函数:
function makeAdder(count) {
    function add(num) {
        return count + num
    }
    return add
}

var add5 = makeAdder(5)
console.log(add5(5));
console.log(add5(10));

2.2、JS中闭包的定义

  • 这里先来看一下闭包的定义,分成两个:在计算机科学中和在 JavaScript 中。
  • 在计算机科学中对闭包的定义(维基百科):
  • 闭包(英语: Closure ),又称词法闭包( Lexical Closure )或函数闭包( function closures );
  • 是在支持头等函数的编程语言中,实现词法绑定的一种技术;
  • 闭包在实现上是一个结构体,它存储了一个函数和一个关联的环境(相当于一个符号查找表);
  • 闭包跟函数最大的区别在于,当捕捉闭包的时候,它的自由变量会在捕捉时被确定,这样即使脱离了捕捉时的上下文,它也能照常运行;
  • 闭包的概念出现于60年代,最早实现闭包的程序是 Scheme ,那么我们就可以理解为什么 JavaScript 中有闭包:
  • 因为 JavaScript 中有大量的设计是来源于 Scheme 的;
  • 我们再来看一下 MDN 对 JavaScript 闭包的解释:
  • 一个函数和对其周围状态( lexical environment ,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包( closure );
  • 也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用城:
  • 在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来;
  • 那么我的理解和总结:
  • 一个普通的函数 function ,如果它可以访问外层作用于的自由变量,那么这个函数就是一个闭包;
  • 从广义的角度来说: JavaScript 中的函数都是闭包;
  • 从狭义的角度来说: JavaScript 中一个函数,如果访问了外层作用域的变量,那么它是一个闭包;

2.3、闭包的访问过程

  • 如果我们编写了如下的代码,它一定是形成了闭包的:
function makeAdder(count) {
    function add(num) {
        return count + num
    }
    return add
}

var add10 = makeAdder(10)
console.log(add10(5));

在这里插入图片描述

2.4、闭包的内存泄漏

  • 那么我们为什么经常会说闭包是有内存泄露的呢?
  • 在上面的案例中,如果后续我们不再使用add10函数了,那么该函数对象应该要被销毁掉,并且其引用着的父作用域 AO 也应该被销毁掉;
  • 但是目前因为在全局作用域下add10变量对0xb00的函数对象有引用,而0xb00的作用域中 AO (0x200)有引用,所以最终会造成这些内存都是无法被释放的;
  • 所以我们经常说的闭包会造成内存泄露,其实就是刚才的引用链中的所有对象都是无法释放的;
  • 那么,怎么解决这个问题呢?
  • 因为当将add10设置为 null 时,就不再对函数对象0xb00有引用,那么对应的 AO 对象0x200也就不可达了;
  • 在 GC 的下一次检测中,它们就会被销毁掉;
add10 = null

在这里插入图片描述

2.5、JS闭包内存泄漏案例

function testArray() {
    var arr = new Array(1024 * 1024).fill(1)
    return function() {
        console.log(arr.length);
    }
}

var arrFns = []
for (var i = 0; i < 100; i++) {
    setTimeout(() => {
        arrFns.push(testArray())
    }, 100 * i)
}

setTimeout(() => {
    for (var i = 0; i < 50; i++) {
        setTimeout(() => {
            arrFns.pop()
        }, 100 * i)
    }
}, 10000)

在这里插入图片描述

2.6、AO不使用的属性

  • 我们来研究一个问题: AO 对象不会被销毁时,是否里面的所有属性都不会被释放?
  • 下面这段代码中 name 属于闭包的父作用城里面的变量;
  • 我们知道形成闭包之后 count 一定不会被销毁掉,那么 name 是否会被销毁掉呢?
  • 这里我打上了断点,我们可以在浏览器上看看结果;
function makeAdder(count) {
    let name = 'why'
    return function add(num) {
        debugger
        return count + num
    }
}

var add5 = makeAdder(5)
console.log(add5(5));
console.log(add5(10));

在这里插入图片描述

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

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

相关文章

数据分析-28-交互式数据分析EDA工具和低代码数据科学工具

文章目录 1 数据分析的七步指南1.1 第一步:问题定义和数据采集1.2 第二步:数据清洗和预处理1.3 第三步:数据探索和分析1.4 第四步:模型建立和分析1.5 第五步:数据可视化1.6 第六步:结果解释和报告1.7 第七步:部署和维护1.8 基础的数据分析库1.9 低代码数据科学工具2 EDA…

yjs09——pandas介绍及相关数据结构

1.什么是pandas 同样&#xff0c;pandas、matplotlib、numpy是python三大库&#xff0c;pandas就像是把matplotlib和numpy结合在一起&#xff0c;让数据以“表格”的形式表现出来&#xff0c;是一个强大的数据处理和分析库&#xff0c;它建立在NumPy库之上&#xff0c;提供了高…

笔试-笔记

前言 记录一下自己遇到的笔试题 1.(单选)下列链表中&#xff0c;其逻辑结构属于非线性结构的是() A.二叉链表 B.双向链表 C.循环链表 D.带链的的栈 解析&#xff1a; 常见线性结构&#xff1a;线性表&#xff0c;栈&#xff0c;队列&#xff0c;双队列&#xff0c;串&…

05-函数传值VS传引用

函数传值 一、没法改变值的方式&#xff1a; 一个变量拷贝到另一个变量, 这种形式的函数调用被称为: 传值调用 局部变量的生命周期在函数的运行期间会一直存在. void Increment(int a)//假设一个 x(只是为了验证实参会被映射到形参这件事情),a的值会被拷贝到x {a a 1; //1…

【d57】【sql】1661. 每台机器的进程平均运行时间

思路 一方面考察自连接&#xff0c;另一方面考察group by 这里主要说明 group by 用法&#xff1a; 1.在 SQL 查询中&#xff0c;GROUP BY 子句用于将结果集中的行分组&#xff0c;目的通常就是 对每个组应用聚合函数&#xff08;如 SUM(), AVG(), MAX(), MIN(), COUNT() 等…

如何理解业务系统的复杂性

鹅厂万人热议&#xff5c;如何理解业务系统的复杂性&#xff1f;-腾讯云开发者社区-腾讯云 腾小云导读 业务系统复杂性一直是令开发者头痛的问题。复杂的不是增加一个需求需要耗费多少时间&#xff0c;而是在增加一个需求后带来的蝴蝶效应&#xff1a;其它功能会不会受到影响、…

MES数据的集成方式

为了实现与其他关键系统的数据共享和协同工作&#xff0c;不同的集成方式应运而生。MES系统与其他系统的常见集成模式&#xff0c;包括封装接口调用模式、直接集成模式、数据聚合模型、中间件集成模式以及XML的信息集成模式等。 1. 封装接口调用模式 封装接口调用是一种常见的…

防反接电路设计

方案1 串联二极管&#xff0c; 优点&#xff1a;成本低、设计简单 缺点&#xff1a;损耗大&#xff0c;P ui 方案2 串联自恢复保险丝 当电源反接的时候&#xff0c;D4导通&#xff0c;F2超过跳闸带你留&#xff0c;就会断开&#xff0c;从而保护了后级电路 方案3 H桥电路…

修改ID不能用关键字作为ID校验器-elementPlus

1、校验器方法 - forbiddenCharValidator const idUpdateFormRef ref(null); const forbiddenCharValidator (rule, value, callback) > {const forbiddenCharacters [as,for,default,in,join,left,inner,right,where,when,case,select];for (let forbiddenCharacter o…

劳动与科技、艺术结合更好提高劳动教育意义

在中小学教育中&#xff0c;劳动教育是培养学生基本生活技能和劳动习惯的重要环节。但当代的劳动教育不在单纯的劳动&#xff0c;而是劳动技能的提升与学习&#xff0c;通过学习劳动技能与实践活动&#xff0c;强化劳动教育与其他课程的融合&#xff0c;学生深刻理解劳动的意义…

python如何判断图片路径是否存在

1、在向文件夹中保存数据前&#xff0c;先判断该文件夹(路径)是否存在。 save_path /root/.../image/result if not os.path.exists(save_path):os.makedirs(save_path) 本来路径里只有到image文件夹的&#xff0c;执行完后会自动在image下创建result文件夹。 2、在打开某些图…

滑动窗口->dd爱框框

1.题目&#xff1a; 2.题解&#xff1a; 2.1为什么用滑动窗口优化&#xff1a; 因为元素都是大于0的 所以&#xff1a;当找到大于等于x的值时&#xff0c;right可以不用返回 两个指针都往后走&#xff1b;因此可以使用滑动窗口优化暴力解法 2.2&#xff1a;滑动窗口具体使用步…

骨传导耳机哪个品牌好用?盘点闭眼入都不踩雷的五大爆款机型!

骨传导耳机是智商税还是真有用&#xff1f;哪款骨传导耳机更值得购买&#xff1f;骨传导耳机作为市场中非常热门的机型&#xff0c;相信很多人都想入手一款&#xff0c;但面对市面鱼龙混杂的耳机品牌&#xff0c;往往不知道从何下手&#xff0c;不过市场重确实存在不少劣质产品…

ubutun nginx 安装和解决端口占用问题

目录 一、删除已有nginx 二、安装nginx 三、端口占用问题 分析问题 解决方法&#xff1a;更换默认端口 nginx是一个高性能的 HTTP 和反向代理 web 服务器&#xff0c;同时也提供了 IMAP/POP3/SMTP 服务。是一款轻量级的 Web 服务器/反向代理服务器及电子邮件&#xff08;I…

Sqoop实战-- Sqoop的Job任务、增量导入、数据格式转换与Lombok的使用指南

数据传输是任何数据驱动型组织的关键时刻。Apache Sqoop 在促进关系型数据库和Hadoop之间的高效数据传输方面表现出色&#xff0c;使其成为大数据工作流程中不可或缺的工具。本文将详细介绍如何使用Sqoop执行Job任务以及进行增量导入&#xff0c;如何在HDFS上指定数据存储格式&…

031集——文本文件按空格分行——C#学习笔记

如下图&#xff0c;读取每行文本&#xff0c;每行文本再按空格分开读取一个字符串&#xff0c;输出到另一个文本&#xff1a; CAD环境下&#xff0c;代码如下&#xff1a; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Runtime; using System; using Sys…

如何使用ssm实现白云会议管理系统+vue

TOC ssm741白云会议管理系统vue 第1章 绪论 1.1 选题动因 到现在为止&#xff0c;互联网已经进入了千家万户&#xff0c;最普通的平民百姓也有属于自己的智能设备&#xff0c;计算机各种技术的储备也是相当的丰富&#xff0c;并且实现也是没有难度&#xff0c;各行各业&…

Gpt4.0最新保姆级教程开通升级

如何使用 WildCard 服务注册 Claude3 随着 Claude3 的震撼发布&#xff0c;最强 AI 模型的桂冠已不再由 GPT-4 独揽。Claude3 推出了三个备受瞩目的模型&#xff1a;Claude 3 Haiku、Claude 3 Sonnet 以及 Claude 3 Opus&#xff0c;每个模型都展现了卓越的性能与特色。其中&a…

【Python报错已解决】TypeError: an integer is required (got type bytes)

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

无人机培训机构配套教学无人机技术详解

无人机培训机构配套的教学无人机技术&#xff0c;是一个涉及多学科交叉、技术密集型的领域。以下是对该技术的详细解析&#xff1a; 一、无人机技术概述 无人机技术是一个涵盖航空工程、电子工程、计算机科学、材料科学和人工智能等多个学科的综合性领域。其核心在于实现无人…