当前位置: 首页 > news >正文

【js(3)】执行上下文/作用域链/垃圾回收与内存泄漏/闭包

执行上下文/作用域链/垃圾回收与内存泄漏/闭包

  • 一、执行上下文
  • 二、作用域链
    • 举例
  • 三、垃圾回收与内存泄漏
    • 1.垃圾回收的概念
    • 2.回收机制
    • 3.垃圾回收的方式
      • (1)标记清除
      • (2)引用计数
    • 4.内存泄漏
  • 四、闭包
    • 1.闭包的理解
    • 2.闭包的用途
    • 3. 举例

  • 为什么几乎所有的编程语言代码都是从上往下执行
  • 为什么所有的编程语言函数外部都访问不到函数内部的变量

一、执行上下文

  • 执行上下文通俗来说就是代码执行所处的当前容器
  • 在js中,大到浏览器,小到一个函数、一个代码块都有自己的上下文
  • 不同的js宿主环境也有不同的全局上下文(一个程序中只有一个全局执行上下文)。例如在浏览器中全局上下文是window,在node中全局上下文是global
  • 用浏览器举例,全局上下文会在你打开一个页面时创建,在退出页面或者关闭浏览器时销毁。
  • 在函数中(当一个函数被调用,就会为该函数创建一个新的执行上下文,函数的上下文可以有任意多个):当代码执行流进入到函数,该函数的上下文被推到上下文栈中,等到函数执行完之后,上下文栈会弹出该函数的上下文,将控制权返还给之前的上下文

在这里插入图片描述

二、作用域链

  • 代码在上下文栈中执行的时候,还伴随着作用域链的创建
  • 作用域链决定了上下文中的代码访问变量的顺序以及权限
  • 在任意上下文中用var定义的一个变量,js会将改变量添加到作用域链中,还会自动挂载为当前上下文对象的一个属性

举例

在这里插入图片描述

1.anotherColor和color并不是swapColors函数中的变量,为什么在当前函数上下文中也能访问到呢?
因为js在访问变量的时候,会优先在当前上下文中查找,如果找不到就沿着作用域链去深层寻找,一直找到全局上下文中都没有的话,就输出underfined。这里的两个变量是可以在作用域链中找到的。

三、垃圾回收与内存泄漏

1.垃圾回收的概念

JavaScript代码运行时,需要分配内存空间来储存变量和值。当变量不在参与运行时,就需要系统收回被占用的内存空间,这就是垃圾回收

2.回收机制

  • Javascript 具有自动垃圾回收机制,会定期对那些不再使用的变量、对象所占用的内存进行释放,原理就是找到不再使用的变量,然后释放掉其占用的内存
  • JavaScript中存在两种变量:局部变量和全局变量。全局变量的生命周期会持续要页面卸载;而局部变量声明在函数中,它的生命周期从函数执行开始,直到函数执行结束,在这个过程中,局部变量会在堆或栈中存储它们的值,当函数执行结束后,这些局部变量不再被使用,它们所占有的空间就会被释放。
  • 局部变量被外部函数使用时,其中一种情况就是闭包,在函数执行结束后,函数外部的变量依然指向函数内部的局部变量,此时局部变量依然在被使用,所以不会回收。

3.垃圾回收的方式

(1)标记清除

  • 标记清除是浏览器常见的垃圾回收方式,当变量进入执行环境时,就标记这个变量“进入环境”,被标记为“进入环境”的变量是不能被回收的,因为他们正在被使用。当变量离开环境时,就会被标记为“离开环境”,被标记为“离开环境”的变量会被内存释放

(2)引用计数

  • 跟踪记录每个值被引用的次数
  • 这种方法会引起循环引用的问题。循环引用中,引用次数永远不可能为0.

在这里插入图片描述

4.内存泄漏

程序在运行过程中,分配了内存空间,但由于某些原因,这些内存无法被释放或回收。

四、闭包

1.闭包的理解

  • 闭包是指有权访问另一个函数作用域中变量的函数

2.闭包的用途

  • 闭包的第一个用途是使我们在函数外部能够访问到函数内部的变量。通过使用闭包,可以通过在外部调用闭包函数,从而在外部访问到函数内部的变量,可以使用这种方法来创建私有变量。
  • 闭包的另一个用途是使已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包函数保留了这个变量对象的引用,所以这个变量对象不会被回收
function foo() {var a = 1; // a 是一个被 foo 创建的局部变量function bar() { // bar 是一个内部函数,是一个闭包console.log(a); // 使用了父函数中声明的变量}return bar();
}
foo(); // 1

foo() 函数中声明了一个内部变量 a , 在函数外部是无法访问的,bar() 函数是 foo() 函数内部的函数,此时 foo 内部的所有局部变量,对 bar 都是可见的,反过来就不行,bar 内部的局部变量,对 foo 就是不可见的。这就是javaScript特有的”作用域链“。

function foo() {var a = 1; // a 是一个被 foo 创建的局部变量function bar() { // bar 是一个内部函数,是一个闭包console.log(a); // 使用了父函数中声明的变量}return bar;
}
const myFoo = foo();
myFoo();

foo() 执行后,将其返回值(也就是内部的 bar 函数)赋值给变量 myFoo 并调用 myFoo(), 实际上只是通过不同的标识符引用调用了内部的函数 bar()。
foo() 函数执行后,正常情况下 foo() 的整个内部作用域被销毁,占用的内存被回收。但是现在的 foo的内部作用域 bar() 还在使用,所以不会对其进行回收。bar() 依然持有对改作用域的引用,这个引用就叫做闭包。

3. 举例

for (var i = 1; i <= 5; i++) {setTimeout(function timer() {console.log(i)}, i * 1000)
}//输出6 6 6 6 6 6
//(1)var是函数作用域,var i是在整个函数中的声明,所有的setTimeout回调函数都共享一个i
//(2)setTimeout是异步的,它等主程序执行完了之后才执行,等他执行的时候i已经=6了

解决一:let

for (let i = 1; i <= 5; i++) {setTimeout(function timer() {console.log(i);}, i * 1000);
}
//let 有块级作用域,每次循环都会创建一个新的 i,每个 setTimeout 都绑定到当前循环的 i

解决二:闭包

for (var i = 1; i <= 5; i++) {;(function (j) {//自执行函数,接收参数j,每次循环都会调用一次这个函数,j的值是当前循环中的isetTimeout(function timer() {console.log(j);}, j * 1000);})(i);//将当前的i作为参数传入
}
http://www.lryc.cn/news/594634.html

相关文章:

  • Vue组件之间通信
  • C语言运算符优先级“潜规则”
  • 数据库的介绍和安装
  • HTTP,HTTPS
  • 文件的写出操作|文件的追加写入操作|文件操作的综合案例
  • mac安装node的步骤
  • IDEA 同时修改某个区域内所有相同变量名
  • 跑腿小程序|基于微信小程序的跑腿平台小程序设计与实现(源码+数据库+文档)
  • Taro 生命周期相关 API 详解
  • Idea或Pycharm上.idea的忽略提交的问题总结
  • Linux初识网络
  • 用 STM32 的 SYSTICK 定时器与端口复用重映射玩转嵌入式开发
  • 分布在内侧内嗅皮层(MEC)的带状细胞对NLP中的深层语义分析有什么积极的影响和启示
  • 微服务的编程测评系统-身份认证-管理员登录前端
  • .NET依赖注入IOC你了解吗?
  • 智能体性能优化:延迟、吞吐量与成本控制
  • 机器阅读理解(MRC)全面解析:任务分类、评估指标与57个数据集资源盘点
  • Nacos安装单例模式
  • 西门子 SIMATIC S7-1500 数字量输入模块:深度剖析与应用指南
  • ABQ-LLM:用于大语言模型的任意比特量化推理加速
  • Zabbix 企业级分布式监控系统深度解析
  • Android 单编 framework 相关产物输出介绍
  • 3.组合式API父子通信
  • OpenAI开发的一款实验性大型语言模型(LLM),在2025年国际数学奥林匹克竞赛(IMO)中达到了金牌水平
  • 什么是商业智能BI数据分析的指标爆炸?
  • 悬镜安全将受邀参加2025开放原子开源生态大会
  • “融合进化,智领未来”电科金仓引领数字化转型新纪元
  • FFmpeg:数字媒体的终极瑞士军刀
  • ssms(SQL 查询编辑器) 添加快捷键 Ctrl+D(功能等于Ctrl+C + Ctrl+V),一步到位
  • 【PTA数据结构 | C语言版】列出连通集