JavaScript 知识点 - 作用域(变量提升、垃圾回收机制、闭包)

news2024/11/18 13:27:29

一、作用域

1、基本概念

是什么?

指变量、对象和函数在【代码中的可访问性范围】。

有什么用?

理解作用域对【编写高效和无错误的代码】至关重要

分类

局部作用域(函数作用域、块作用域)、全局作用域

涉及到那些知识点

作用域链、JS垃圾回收机制、闭包、变量提升等。

2、全局作用域

是什么?

在代码的任何地方都可以访问的变量和函数

怎么产生?

(1)【<script>标签和.js文件的最外层】声明的变量和函数

(2)【window 对象动态添加的属性】默认也是全局的

注意事项

实际开发中尽可能少的声明全局变量、防止全局变量被污染

3、局部作用域

【1】函数作用域

是什么?

只能在【变量或函数 所处的 函数内部】被访问的变量和函数

有什么用?

用于限制变量的可见性,避免与【其他函数或全局作用域中的变量产生冲突】

怎么产生?

(1)【函数内部】声明的变量和函数

(2)【函数的参数】 也是函数内部的局部变量

【2】块作用域

是什么?

块级作用域是【指在 {} 代码块内部】声明的变量(var声明的除外var 声明的变量不会产生块级作用域),在该块外部无法访问。

怎么产生?

【在 {} 代码块内部】声明的变量(var声明的除外)

示例

// var 声明变量 不形成块级作用域、 i 是全局作用域的
    for(var i = 1; i < 10; i++) {
      console.log(i, 'i');
      setTimeout(() => {
        console.log(i, 'setTimeout i');
      }, 100)
    }
    
    // let 声明变量 形成块级作用域
    for(let i = 1; i < 10; i++) {
      console.log(i, 'i');
      setTimeout(() => {
        console.log(i, 'setTimeout i');
      }, 100)
    }

第一段代码输出结果

  • 先打印 1 到 9,每个数字后面跟着 'i'
  • 当 setTimeout 的回调函数执行时,由于 var 使得 i 是全局作用域的,最终 i 的值是 10,所以将会打印 10,并且会重复打印 9 次。

第二段代码输出结果

  • 先打印 1 到 9,每个数字后面跟着 'i'
  • 由于 let 使得 i 在每次循环迭代中都具有块作用域,因此在 setTimeout 执行时,每个回调函数的 i 都是当前迭代的值。

二、作用域链

基本概念

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

查找原则

在函数被执行时、会优先查找【当前函数作用域中查找变量

同一作用域内,变量按【声明的顺序进行查找】。

如果当前作用域查找不到则会【依次逐级查找父级作用域直到全局作用域

三、变量提升

1、基本概念

        变量提升 JavaScript 的一个机制JavaScript 引擎在执行代码时,会先查找 函数 和 var 声明的变量 提升到当前所处作用域顶部。

        ***变量提升只提升声明,不提升赋值***

2、注意点

函数提升

(1)  函数表达式不存在提升的现象

(2)  函数提升出现在相同的作用域中

变量提升

(1) 变量在未声明即被访问时会报语法错误

(2) 变量在 var 声明之前即被访问、变量的值为 undefined

(3) let/const 声明的变量不存在变量提升

(4) 变量提升出现在当前相同作用域当中

(5) 实际开发中推荐先声明再访问变量

3、DEMO 

// 全局作用域
    var globalVar = "I'm a global variable";

    function testHoisting() {
      console.log(globalVar); // 输出: "I'm a global variable"
      
      // 变量提升
      console.log(localVar); // 输出: undefined (因为声明被提升)
      var localVar = "I'm a local variable";
      console.log(localVar); // 输出: "I'm a local variable"

      // 函数提升
      console.log(innerFunction()); // 输出: "Inner function called!"

      function innerFunction() {
          return "Inner function called!";
      }

      // let 和 const 的提升
      try {
          console.log(letVar); // ReferenceError: Cannot access 'letVar' before initialization
      } catch (e) {
          console.log(e.message); // 捕获错误信息
      }
      
      let letVar = "I'm a let variable";
      const constVar = "I'm a const variable";
      
      console.log(letVar);  // 输出: "I'm a let variable"
      console.log(constVar); // 输出: "I'm a const variable"
    }

    testHoisting();

    console.log(globalVar); // 输出: "I'm a global variable"

四、JavaScript 的垃圾回收机制

基本概念

        JavaScript 的垃圾回收机制是指【JavaScript引擎会自动检测和清理不再使用的内存空间】,以【防止内存泄漏和提高性能】。JavaScript 使用一种自动内存管理的方式,开发者无需手动释放内存。
 

内存的生命周期

(1) 【内存分配】: 当我们声明变量、函数、对象的时候、系统会自动为他们分配内存

(2) 【内存使用】: 即读写内存、也就是使用变量、函数等

(3) 【内存回收】: 使用完毕、由【垃圾回收器】自动回收不再使用的内存

注意点

(1) 全局变量一般不会回收(关闭页面回收)

(2) 一般情况下【局部变量的值】、不用了、会被【自动回收】掉

内存泄漏

程序中分配的【内存】由于某种原因程序【未释放】或【无法释放】叫做内存泄漏

JavaScript 垃圾回收机制算法

1. 引用计数法 (早期少数浏览器使用)

原理:
  • 每个对象维护一个引用计数器,记录有多少个引用指向该对象。
  • 当一个对象被引用时,引用计数加一;当引用被移除时,引用计数减一。
  • 当引用计数为零时,说明没有任何引用指向该对象,可以安全地释放它的内存。
优点:
  • 实时性:能立即回收不再使用的对象,减少内存占用。
缺点:
  • 循环引用问题:如果两个对象相互引用,即使它们不再被其他对象引用,它们的引用计数也不会变为零,从而导致内存泄漏。

2. 标记清除法 (大多数现在浏览器使用)

原理:
  • 标记阶段:从根对象(如全局对象、活动函数的变量等)开始,递归地遍历所有可达的对象,并将其标记为“活着”。
  • 清除阶段:遍历所有对象,将那些未被标记的对象视为“死去”的,释放它们所占用的内存
优点:
  • 能够处理循环引用的问题,因为只要对象不可达,就会被回收。
缺点:
  • 性能开销:标记和清除的过程可能会消耗较多的 CPU 资源,导致应用程序在垃圾回收期间可能出现性能波动。

五、闭包

基本概念

        闭包(closure)是一个函数以及其捆绑的周边环境状态的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。

        简单来讲、一个函数 内部函数 引用了 外部函数的变量就会形成闭包。

作用

1、" 保护"作用

说明:

闭包可以创建私有变量,避免外部访问,从而保护变量不被随意修改。这种封装特性使得数据更安全,减少了全局作用域污染。

示例:

count 是一个私有变量,只有通过 incrementdecrementgetCount 方法才能访问和修改,保护了变量的安全性。

function createCounter() {
    let count = 0; // 私有变量

    return {
        increment: function() {
            count++;
            console.log(count);
        },
        decrement: function() {
            count--;
            console.log(count);
        },
        getCount: function() {
            return count;
        }
    };
}

const counter = createCounter();
counter.increment(); // 输出: 1
counter.increment(); // 输出: 2
console.log(counter.getCount()); // 输出: 2
// 直接访问 count 是不可能的
// console.log(counter.count); // undefined

2、" 保存"作用

说明:

        闭包能够保存外部函数的变量状态,即使外部函数已经执行完毕。这使得我们可以在异步操作或定时器中保留某个状态。

示例:

   timerId 被闭包保护并保存。即使 makeTimer 函数执行结束,timerId 的值依然可以被 startstop 方法访问和修改。

function makeTimer() {
    let timerId = 0;

    return {
        start: function() {
            timerId = setInterval(() => {
                console.log(`Timer ID: ${timerId}`);
            }, 1000);
        },
        stop: function() {
            clearInterval(timerId);
            console.log('Timer stopped');
        }
    };
}

const timer = makeTimer();
timer.start(); // 每秒输出 Timer ID
setTimeout(() => {
    timer.stop(); // 3秒后停止计时器
}, 3000);

产生的问题

内存泄漏

        闭包会保持对其外部作用域的引用,javaScript 的垃圾回收机制无法清除释放内存。从而产生内存泄漏问题、故使用闭包时需及时清理引用。

function createCounter() {
    let count = 0;

    return {
        increment: function() {
            count++;
        },
        getCount: function() {
            return count;
        }
    };
}

const counter = createCounter();
// 使用完后,清理引用
counter.increment = null;

变量状态混淆

        当多个闭包引用同一个外部变量时,可能会导致所有闭包共享该变量的最终值。

const functions = [];

for (var i = 0; i < 3; i++) {
    functions.push(function() {
        console.log(i);
    });
}

// 输出结果是 3, 3, 3
functions.forEach(func => func());

性能问题

        过度使用闭包可能会导致性能下降。每次创建闭包都会产生额外的函数和作用域,过多的闭包可能导致内存和性能的开销增加。

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

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

相关文章

在线支付系统

一、系统概述 本在线支付系统基于 Servlet 技术构建&#xff0c;旨在为用户提供安全、便捷的支付服务。系统具备简洁的用户界面和高效的支付处理能力&#xff0c;满足用户在各种场景下的支付需求。 二、主要功能 首页登录注册&#xff1a; 用户可以在首页进行登录和注册操作。注…

MacBook 使用 brew 安装 MySQL

目录 &#xff08;1&#xff09;准备工作1.1 更新 brew &#xff08;2&#xff09;正式安装2.1 安装MySQL&#xff1a;2.2 启动mysql &#xff08;3&#xff09;初始化数据库3.1 选择验证密码组件3.2 密码强度3.3 删除匿名用户3.4 禁用root用户远程连接3.5 删除test数据库3.6 重…

C语言 17 宏定义

前面认识了#include指令&#xff0c;接着来看#define指令&#xff0c;它可以实现宏定义。宏是啥意思&#xff1f; 把参数批量替换到文本中&#xff0c;这种实现通常称为宏&#xff08;macro&#xff09;或定义宏 (define macro) 可以通过#define来定义宏&#xff0c;规则如下&a…

Cyber Weekly #26

赛博新闻 1、Meta发布最强AR眼镜 Meta Connect 2024大会展示了多款新产品和技术&#xff0c;包括更便宜的Quest 3S系列AR眼镜、新功能丰富的Meta Rayban眼镜、OrionAR眼镜原型机&#xff0c;以及月活5亿用户的Meta AI。其中&#xff0c;OrionAR眼镜以其先进的交互体验和强大的…

鸿蒙开发(NEXT/API 12)【硬件(传感器开发3)】传感器服务

场景介绍 当设备需要获取传感器数据时&#xff0c;可以使用sensor模块&#xff0c;例如&#xff1a;通过订阅方向传感器数据感知用户设备当前的朝向&#xff0c;通过订阅计步传感器数据统计用户的步数等。 函数说明 名称描述OH_Sensor_GetInfos(Sensor_Info **infos, uint32…

算力运力解决方案:构建未来智算新生态

中国联通国际有限公司产品之算力运力解决方案&#xff1a;构建未来智算新生态 在当今这个数据爆炸、技术日新月异的时代&#xff0c;算力已成为推动社会进步和产业升级的关键力量。中国联通国际有限公司紧跟时代步伐&#xff0c;依托其强大的网络资源和深厚的技术积累&#xf…

Linux进程间的通信(四)System-V共享内存

什么是共享内存 共享内存&#xff0c;顾名思义就是允许两个不相关的进程访问同一个逻辑内存&#xff0c;共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。 不同进程之间共享的内存通常为同一段物理内存。进程可以将同一段物理内存连接到他们自己的地址空…

深度学习后门攻击分析与实现(二)

前言 在本系列的第一部分中&#xff0c;我们已经掌握了深度学习中的后门攻击的特点以及基础的攻击方式&#xff0c;现在我们在第二部分中首先来学习深度学习后门攻击在传统网络空间安全中的应用。然后再来分析与实现一些颇具特点的深度学习后门攻击方式。 深度学习与网络空间…

Node.js安装Express,Node.js支持Typescript以及Express支持Typescript的步骤

1. Node.js 安装Express 运行如下命令&#xff1a; $ mkdir express-demo $ cd express-demo$ npm install express $ npm install body-parser //(可选)中间件&#xff0c;用于处理 JSON, Raw, Text 和 URL 编码的数据 $ npm install cookie-parser //(可选)通过req.cookies…

怎么用gitee做一个图片仓库,在md文档中用这个图片网络地址,然后显示图片

痛因&#xff1a;我为什么要这样做&#xff0c;呃&#xff0c;我一开始图片都是存本地地址的&#xff0c;放在和这个md文档同级的assets文件夹下面&#xff0c;这样子确实当时很方便&#xff0c;复制粘贴什么也不用管&#xff0c;但是想把这个文档分享给别的人的时候&#xff0…

Windows打开HDF5图像:HDFView软件的下载、安装

本文介绍在Windows电脑中&#xff0c;下载、安装用以查看HDF5图像数据的软件HDFView的方法。 HDF5&#xff08;Hierarchical Data Format 5&#xff09;是一种用于存储和管理大量科学数据的文件格式&#xff0c;其由HDF Group开发和维护&#xff0c;广泛应用于科学计算、工程、…

ESP8266wifi模块的使用

文章目录 概要整体架构流程技术名词解释具体配置过程小结 概要 本文旨在介绍ESP8266,做为客户端 电脑做为服务端&#xff0c;通过TCP/IP协议在同一个局域网下通过WiFi进行数据交互 设备选用 esp8266 软件选择 安信可串口调试助手 网络调试助手 &#xff08;若没有软件可私…

OpenCV threhold()函数

OpenCV threhold()函数的主要用途是将灰度图转换为二值图像,实现灰度图的二值化&#xff0c;在机器视觉中使用频度较高&#xff0c;如尺寸量测&#xff0c;物体识别等。其原型如下&#xff1a; 函数参数&#xff1a; src 输入数组&#xff08;多通道、8 位或 32 位浮点&#xf…

SpringBoot3脚手架

MySpringBootAPI SpringBoot3脚手架&#xff0c;基于SpringBoot3DruidPgSQLMyBatisPlus13FastJSON2Lombok&#xff0c;启动web容器为Undertow(非默认tomcat)&#xff0c;其他的请自行添加和配置。 <java.version>17</java.version> <springboot.version>3.3…

项目集成SpringSecurity框架

目录 项目没集成SpringSecurity框架的实现 项目之前的登录接口 LoginReqVo 接收前端的数据类型 LoginRespVo返回给前端的数据 项目集成SpringSecurity 第一步:导入依赖 第二步:创建security包结构 第三步&#xff1a;实现认证过滤器 第一步&#xff1a;自定义认证过滤器…

Modbus调试工具和源码分享

我们应该知道了学习Modbus协议应该具备主从两个设备才行&#xff0c;但是在学习过程中如果没有真实的物理设备&#xff0c;应该怎么调试呢&#xff1f; 我们可以通过软件工具来模拟主从设备&#xff0c;下面我们推荐几个比较实用的工具。 以下内容包含&#xff1a;实用工具、…

超好用的10款视频剪辑软件,从入门到精通

视频剪辑软件哪款比较好呢&#xff1f;无论是专业制作团队、自媒体创作者&#xff0c;还是家庭用户&#xff0c;一款好用的视频剪辑软件都能极大地提升创作效率和作品质量。以下是十款备受推崇的视频剪辑软件&#xff0c;分别从适用人群、易用程度和功能特点进行介绍。 1.影忆…

揭秘移动硬盘RAW:原因、恢复策略与预防措施

移动硬盘RAW概述 移动硬盘&#xff0c;作为现代数据存储的重要工具&#xff0c;其稳定性与数据安全直接关乎用户的数据资产安全。然而&#xff0c;在使用过程中&#xff0c;不少用户会遇到移动硬盘状态突然变为RAW格式的情况&#xff0c;这往往伴随着数据无法直接访问的困扰。…

1688客服代码怎么做生成悬浮客服代码阿里巴巴国内站1688平台悬浮特效悬浮代码悬浮客服 1688客服代码怎么做生成器软件代码工具制作客服代码阿里巴巴

阿里巴巴国内站1688平台悬浮特效悬浮代码悬浮客服 1688客服代码怎么做生成器软件代码工具制作客服代码阿里巴巴 一秒美工工具

王道-操作系统

3 下列说法正确的是_____ 答案:A 解析: A 正确。如链接文件可以顺序存取,但不能随机存取。连续文件可随机存取,也可顺序存取。 B 错误。一个 FCB 就是一个文件目录项。在引入索引节点后,每个文件的目录项只保留文件名和指向该文件对应的索引节点指针,而索引节点的有关信息…