【JavaScript】二十九、垃圾回收 + 闭包 + 变量提升

news2025/7/15 2:28:00

文章目录

  • 1、作用域
    • 1.1 局部作用域
    • 1.2 全局作用域
    • 1.3 作用域链
  • 2、JC垃圾回收机制♻️
  • 3、GC算法
    • 3.1 引用计数法
    • 3.2 标记清除法
  • 4、闭包
    • 4.1 定义
    • 4.2 闭包的应用:实现数据的私有
  • 5、变量提升

1、作用域

即一个范围,离开了这个范围,这个变量就不能再被访问到

1.1 局部作用域

又分为:

  • 函数作用域
  • 块作用域

函数作用域:

  • 函数内部声明的变量,在函数外部无法被访问
  • 函数的形参也是函数内部的局部变量

在这里插入图片描述

块作用域:

JS中,使用 { } 包裹的代码称为代码块,代码块内部声明的变量外部将有可能无法被访问

在这里插入图片描述

  • let 声明的变量会产生块作用域,var 不会产生块作用域
  • const 声明的常量也会产生块作用域
  • 不同代码块之间的变量无法互相访问
  • 推荐使用 let 或 const

1.2 全局作用域

即script 标签 和 .js 文件 的 最外层声明的变量

在这里插入图片描述

  • 为 window 对象动态添加的属性默认也是全局的,不推荐!
  • 函数中未使用任何关键字声明的变量为全局变量,不推荐!
<body>
    <script>
        function m1() {
            num = 10
        }

        // 调用一下
        m1()
        // 成功拿到了10
        console.log(num)
    </script>
</body>
<body>
    <script>
        window.myVar = '自定义'
        // 自定义
        console.log(window.myVar)
    </script>
</body>

1.3 作用域链

输出结果:2
在这里插入图片描述

作用域链本质上是底层的变量查找机制

  • 在函数被执行时,会优先查找当前函数作用域中查找变量(就近)
  • 如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域,上面例子中,就存在g函数作用域 --> f函数作用域 --> 全局作用域这个链路

2、JC垃圾回收机制♻️

整个过程:

  • 内存分配:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存
  • 内存使用:即读写内存,也就是使用变量、函数等
  • 内存回收:使用完毕,由垃圾回收自动回收不再使用的内存,即GC
<body>
    <script>
        // 为变量分配内存
        const i = 1
        const str = 'test'

        // 为对象分配内存
        const obj = {
            uname: 'tom',
            age: 18
        }

        // 为函数分配内存
        function sum(a, b) {
            return a + b
        } 
    </script>
</body>
  • 全局变量一般不会回收(关闭页面回收)
  • 一般情况下局部变量的值, 不用了, 会被自动回收掉
  • 如果不再用到的内存,没有及时释放,就叫做内存泄漏

3、GC算法

3.1 引用计数法

IE采用的引用计数算法, 定义“内存不再使用”,就是看一个对象是否有指向它的引用,没有引用了就回收对象 算法:

  • 跟踪记录被引用的次数
  • 如果被引用了一次,那么就记录次数1,多次引用会累加 ++
  • 如果减少一个引用就减1 –
  • 如果引用次数是0 ,则释放内存
// 引用+1
const arr = [1, 2, 3, 4]
// 引用-1
arr = null

该算法的缺陷时,出现循环引用时,会导致内存泄漏

在这里插入图片描述
如上,即使执行o1 = null 和 o2 = null,o1和o2也不会被回收,因为引用还剩1

3.2 标记清除法

现代浏览器通用的大多是基于标记清除算法的某些改进算法,核心思路:

  • 标记清除算法将“不再使用的对象”定义为“无法达到的对象”
  • 就是从根部(在JS中就是全局对象)出发定时扫描内存中的对象,凡是能从根部到达的对象,都是还需要使用的
  • 那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收

在这里插入图片描述
对引用计数的循环引用问题,标记清除算法就不会有这个问题,和上图类似,执行o1 = null 和 o2 = null后,o1和o2就是一个不可达的状态了

4、闭包

4.1 定义

一个函数对周围状态的引用捆绑在一起,且内层函数中访问到其外层函数的作用域,就是闭包,简单说:闭包 = 内层函数 + 外层函数的变量,如下,内层函数f访问了外层函数outer的变量a,因此,内层函数和外层函数的变量a形成闭包

<body>
    <script>
        function outer() {
            const a = 1
            function f() {
                console.log(a)
            }
            f()
        }
        outer()
    </script>
</body>

打个断点,然后刷新页面,卡在断点出后,可以看到作用域Scope里有个Closure,即闭包

在这里插入图片描述

闭包的作用:封闭数据,提供操作,外部也可以访问到函数内部的变量

<body>
    <script>
        function outer() {
            const a = 1
            // 定义一个函数
            function f() {
                console.log(a)
            }
            // 返回一个函数,不是调用,加了小括号的f()是调用
            // 调用outer,就返回函数f
            return f
        }

        // 函数
        const fun = outer()
        // 调用这个函数,就在外部拿到了outer函数内部的变量
        // 打印1
        fun()

    </script>
</body>

在这里插入图片描述

4.2 闭包的应用:实现数据的私有

实现统计函数的调用次数:

<body>
    <script>
        let i = 0
        function fn() {
            console.log(++i)
        }

    </script>
</body>

能实现功能,但这个i 是个全局变量,很容易被修改

在这里插入图片描述

改一下,用局部变量和闭包:

<body>
    <script>
        function count() {
            let i = 0
            function fn() {
                // 这里当然也可以简写成console.log(++i)
                i++
                console.log(i)
            }
            return fn
        }

        const fun = count()

    </script>
</body>

在这里插入图片描述
这样就实现了数据私有,无法直接修改,当然你别说这调用fun函数不还是修改了,这里体现了是不能被直接随意修改,fun函数里,你可以自定义权限校验代码,允许条件满足时再改

在这里插入图片描述

可以看出,局部变量i即使函数执行完也没有被回收,这也契合了标记清除算法的原理:从根部对象(JS的全局对象)出发,有一条引用链可达,因此不被回收,那既然这样,也就看出了闭包的另一个风险:可能导致内存泄漏(因为全局变量页面关闭时回收,那就可能存在一条一直可达的引用链)

5、变量提升

变量提升是JS中比较奇怪的现象,它允许在变量声明之前即被访问,当然仅存在于var声明变量

<body>
    <script>
        // undefined
        console.log(num)
        var num = 10

    </script>
</body>
<body>
    <script>
        // 报错 Cannot access 'num' before initialization
        console.log(num)
        let num = 10

    </script>
</body>

变量提升,是指在代码执行前,内部会把当前作用域下,所有var声明的变量,提到当前作用域的最前面,只提升声明的代码,不提升赋值的代码,因此,就出现了上面的undefined,上面的代码,在变量提升后,其实就是下面的代码:

<body>
    <script>
        // undefined
        var num
        console.log(num)
        num = 10

    </script>
</body>

JS变量提升的过程:

  • 先把var 变量提升到当前作用域于最前面
  • 只提升变量声明, 不提升变量赋值
  • 然后依次执行代码

如下,变量提升,是提到当前作用域的前面,var num的作用域是fn函数,而这里是全局作用域

<body>
    <script>
        function fn() {
            // undefined
            console.log(num)
            var num = 10
        }
        fn()
        // 这里就会报错了,num is not defined
        // 变量提升,是提到本作用域的前面,var num的作用域是fn函数,而这里是全局作用域
        console.log(num)
    </script>
</body>

总结:

  • 变量在未声明即被访问时会报语法错误,但var声明的变量,在声明之前即被访问,变量的值为 undefined,并不会报错
  • let/const 声明的变量不存在变量提升
  • 变量提升出现在相同作用域当中
  • 当然,实际开发推荐先定义再使用,不建议使用var声明变量

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

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

相关文章

【从零开始学习RabbitMQ | 第一篇】从异步通信到交换机

目录 前言 1.什么是RabbitMQ&#xff1f; 2.同步调用的优缺点 3.异步调用的优缺点 3.1优点&#xff1a; 3.2异步调用的问题是什么&#xff1f; 4技术选型 4.1AMQP协议就是&#xff1a; 4.2kafka和RabbitMQ的使用场景 5.安装RabitMq 6.rabitmq的整体架构 7.RabibtM…

AI(学习笔记第二课) 使用langchain进行AI开发

文章目录 AI(学习笔记第二课) 使用langchain进行AI开发学习内容&#xff1a;1. 使用背景2.创建python&#xff08;pycharm community版&#xff09;开发环境并连接deepseek2.1 创建python&#xff08;pycharm community版&#xff09;开发环境2.2 创建python工程2.3 写入初始py…

基于Jenkins的DevOps工程实践之Jenkins共享库

文章目录 前言Jenkins共享库结构1、共享库演示2、知识点补充3、实践使用共享库格式化输出日志4、groovy基础语法4.1、 什么是 Groovy&#xff1f;4.2、groovy特点4.3、运行方法4.4、标识符4.5、基本数据类型4.5.1、string类型4.5.2、list类型 4.6、函数使用4.7、正则表达式 5、…

使用Qt自带的Qt assistant时如何添加需要查看的文档

当我们双击打开Qt Assistant时 左边目录栏只有自带的帮助文档&#xff0c;所以需要添加要查看的文档 点击左上角Edit中的Preferences&#xff0c;点击add 找到qdoc文件夹 全选里面的内容 点击Apply 点击ok 左边的目录栏就出现所有这个版本的Qt有关的文档啦

基于网络爬虫+Spark+Hadoop等大数据和SpringBoot技术实现的的汽车行业大数据分析与可视化平台系统(源码+论文+PPT+部署文档教程等)

博主介绍&#xff1a;CSDN毕设辅导第一人、全网粉丝50W,csdn特邀作者、博客专家、腾讯云社区合作讲师、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围…

日本IT|AI应用工程师主要工作内容以及职业前景解析

1. 主要工作内容 AI应用工程师是&#xff1a; 类别具体工作内容常见工具需求分析和业务部门沟通&#xff0c;明确「用AI解决什么问题」PowerPoint, Excel, Miro模型选型与微调用现成AI&#xff08;如BERT、YOLOv8、Stable Diffusion等&#xff09;做Fine-TuningPython (PyTor…

Soft Mask(软遮罩)技术

一、概述 Soft Mask是一种技术或工具&#xff0c;主要用于实现平滑的边缘遮罩效果。它在不同的应用领域有不同的实现和定义 1.在Unity UI设计中 SoftMask是一款专为Unity设计的高级遮罩工具&#xff0c;它突破了传统Mask的限制&#xff0c;提供了更为灵活和细腻的UI遮罩解决方案…

ESP32开发之freeRTOS的互斥量

什么是互斥量互斥量的应用场合互斥量的API函数基本代码结构互斥量使用举例递归锁递归锁举例总结什么是互斥量 在freeRTOS中,多个任务访问一块共享资源,会产生竞争现象。 比如马路上只有一个很早以前的电话亭,A、B都想要打电话,然后他们就开始打架了。但是如果A先进去了然…

K8s 资源分类

K8s 资源分类图谱 内置资源的分类 1、工作负载相关&#xff1a; Pod&#xff1a;最小的部署单元&#xff0c;包含一个或多个容器。 Deployment&#xff1a;管理无状态应用的副本和滚动更新。 StatefulSet&#xff1a;适用于有状态应用&#xff08;如数据库&#xff09;&#…

基于 Flask的深度学习模型部署服务端详解

基于 Flask 的深度学习模型部署服务端详解 在深度学习领域&#xff0c;训练出一个高精度的模型只是第一步&#xff0c;将其部署到生产环境中&#xff0c;为实际业务提供服务才是最终目标。本文将详细解析一个基于 Flask 和 PyTorch 的深度学习模型部署服务端代码&#xff0c;帮…

【金仓数据库征文】金仓数据库 KES:MySQL 迁移实用指南

我们都知道&#xff0c;现在企业数字化转型那可是势在必行&#xff0c;数据库迁移这事儿就变得特别关键。金仓数据库的 KingbaseES&#xff08;简称 KES&#xff09;&#xff0c;就给咱从 MySQL 往 KES 迁移数据库提供了一套超好用的方案。下面咱就讲下 咋用金仓数据库来完成这…

多态(c++详细版)

一.多态 1.1 多态的概念 多态(polymorphism)的概念&#xff1a;通俗来说&#xff0c;就是多种形态。多态分为编译时多态(静态多态)和运⾏时多态(动态多态)&#xff0c;这⾥我们重点讲运⾏时多态&#xff0c;编译时多态(静态多态)和运⾏时多态(动态多态)。编译时多态(静态多态)主…

数据结构——二叉树和堆(万字,最详细)

目录 1.树 1.1 树的概念与结构 1.2 树相关的术语 1.3 树的表示法 2.二叉树 2.1 概念与结构 2.2 特殊的二叉树 2.2.1 满二叉树 2.2.2 完全二叉树 2.3 二叉树存储结构 2.3.1 顺序结构 2.3.2 实现顺序结构二叉树 2.3.2.1 堆的概念与结构 2.3.2. 2 堆的插入与删除数据…

MATLAB基于格拉姆角场与2DCNN-BiGRU的轴承故障诊断模型

本博客来源于CSDN机器鱼&#xff0c;未同意任何人转载。 更多内容&#xff0c;欢迎点击本专栏目录&#xff0c;查看更多内容。 目录 0 引言 1 格拉姆角场原理 2 2DCNN-BiGRU网络结构 3 应用实例 3.1 数据准备 3.2 格拉姆角场数据提取 3.3 网络模型搭建-重中之重 3.4 …

正点原子IMX6U开发板移植Qt时出现乱码

移植Qt时出现乱码 1、前言2、问题3、总结 1、前言 记录一下正点原子IMX6U开发板移植Qt时出现乱码的解决方法&#xff0c;方便自己日后回顾&#xff0c;也可以给有需要的人提供帮助。 2、问题 用正点原子IMX6U开发板移植Qt时移植Qt后&#xff0c;sd卡里已经存储了Qt的各种库&…

JVM局部变量表和操作数栈的内存布局

局部变量表和操作数栈 首先看一段Java源码 public class Add_Sample{public int add(int i, int j){int k 100;int result i j k;return result;}public static void main(String[] args){int result new Add_Sample().add(10,20);System.out.println(result);} }使用ja…

Mockoon 使用教程

文章目录 一、简介二、模拟接口1、Get2、Post 一、简介 1、Mockoon 可以快速模拟API&#xff0c;无需远程部署&#xff0c;无需帐户&#xff0c;免费&#xff0c;跨平台且开源&#xff0c;适合离线环境。 2、支持get、post、put、delete等所有格式。 二、模拟接口 1、Get 左…

使用 IDEA + Maven 搭建传统 Spring MVC 项目的详细步骤(非Spring Boot)

搭建Spring MVC项目 第一步&#xff1a;创建Maven项目第二步&#xff1a;配置pom.xml第三步&#xff1a;配置web.xml第四步&#xff1a;创建Spring配置文件第五步&#xff1a;创建控制器第六步&#xff1a;创建JSP视图第七步&#xff1a;配置Tomcat并运行目录结构常见问题解决与…

3.2.3 掌握RDD转换算子 - 4. 按键归约算子 - reduceByKey()

在本节课中&#xff0c;我们深入学习了Spark RDD的reduceByKey()算子。reduceByKey()主要用于处理元素为(key, value)形式的RDD&#xff0c;能够将相同key的元素聚集并合并&#xff0c;最终返回一个新RDD&#xff0c;其元素类型与原RDD保持一致。通过案例演示&#xff0c;我们首…

Pandas比MySQL快?

知乎上有人问&#xff0c;处理百万级数据&#xff0c;Python列表、Pandas、Mysql哪个更快&#xff1f; Pands是Python中非常流行的数据处理库&#xff0c;拥有大量用户&#xff0c;所以拿它和Mysql对比也是情理之中。 实测来看&#xff0c;MySQL > Pandas > Python列表…