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

从js闭包谈到作用域、作用域链、执行上下文、内存管理

文章目录

    • 作用域
    • 函数作用域和全局作用域
    • 块级作用域和暂时性死区
    • 执行上下文和调用栈
    • 代码执行的两个阶段
    • 调用栈
    • 闭包
    • 内存管理
    • 内存泄漏场景举例
    • 浏览器垃圾回收
    • 如何避免内存泄漏
    • 如何利用闭包实现单例模式

✍创作者:全栈弄潮儿
🏡 个人主页: 全栈弄潮儿的个人主页
🏙️ 个人社区,欢迎你的加入:全栈弄潮儿的个人社区
📙 专栏地址,欢迎订阅:前端架构师之路

闭包是 JavaScript 中最基本也是最重要的概念之一。可是闭包又绝对不是一个单一的概念: 它涉及作用域、作用域链、执行上下文、内存管理等多重知识点。

作用域

作用域其实就是一套规则:这个规则用于确定在特定场景下如何查找变量 。任何语言都有作用域的概念,同一种语言在演进过程中也会不断完善其作用域规则。比如,在 JavaScript 中,ES6 出现之前只有函数作用域和全局作用域之分。

函数作用域和全局作用域

大家应该非常熟悉函数作用域

function foo() { var a = 'bar' console.log(a) } foo()

执行 foo 函数时,变量 a 在函数 foo 作用域内,函数体内可以正常访问,并输出 bar。
而当:

var b = 'bar' function foo() { console.log(b) } foo()

执行这段代码时,foo 函数在自身函数作用域内并未查找到 b 变量,但是它会继续向外扩大查找范围,因此可以在 全局作用域 中找到变量 b,输出 bar。

如果我们稍加改动:

function bar() { var b = 'bar' } function foo() { console.log(b) } foo()

执行这段代码时,foo 和 bar 分属于两个彼此独立的函数作用域 ,foo 函数无法访问 bar 函数中定义的变量 b,且其作用域链内(上层全局作用域中)也不存在相应的变量,因此报错:Uncaught ReferenceError: b is not defined。

总结 :

在 JavaScript 执行一段函数时,遇见变量读取其值,这时候会“就近”先在函数内部找该变量的声明或者赋值情况。这里可能会涉及“变量声明方式”以及“变量提升”。如果在函数内无法找到该变量,就要跳出函数作用域,到更上层作用域中查找。这里的“更上层作用域”可能也是一个函数作用域,

例如:

function bar() { var b = 'bar' function foo() { console.log(b) } foo() } bar()

在 foo 函数执行时,对于变量 b 的声明或读值情况是在其上层函数 bar 作用域中获取的。

同时“更上层作用域”也可以顺着作用域范围向外扩散,一直找到全局作用域:

var b = 'bar' function bar() { function foo() { console.log(b) } foo() } bar()

我们看到,变量作用域的查找是一个扩散过程,就像各个环节相扣的链条,逐次递进,这就是作用域链说法的由来。

块级作用域和暂时性死区

作用域概念不断演进,ES6 增加了 let 和 const 声明变量的块级作用域,使得 JavaScript 中作用域范围更加丰富。块级作用域,顾名思义,作用域范围限制在代码块中,这个概念在其他语言里也普遍存在。当然这些新特性的添加,也增加了一定的复杂度,带来了新的概念,比如 暂时性死区 。

暂时性死区,还需要从“变量提升”说起,参看以下代码:

function foo() { console.log(bar) var bar = 3 } foo()

会输出:undefined,原因是变量 bar 在函数内进行了提升。相当于:

function foo() { var bar console.log(bar) bar = 3 } foo()

但在使用 let 声明时:

function foo() { console.log(bar) let bar = 3 } foo()

会报错:Uncaught ReferenceError: bar is not defined。

我们知道使用 let 或 const 声明变量,会针对这个变量形成一个封闭的块级作用域, 在这个块级作用域当中,如果在声明变量前访问该变量,就会报referenceError 错误 ;如果在声明变量后访问,则可以正常获取变量值:

http://www.cnnetsun.cn/news/3955227.html

相关文章:

  • KernelSU WebUI终极指南:让Android模块管理像打开网页一样简单
  • WeKnora:5分钟快速上手的终极RAG智能文档处理框架,彻底告别信息碎片化
  • 好“哪些资料能入库”
  • GSE:魔兽世界终极宏编辑器完整指南 - 告别繁琐按键操作
  • notify开发实战:如何为新平台贡献适配器?
  • Protolint完全指南:如何用这款插件化工具统一你的Protocol Buffer代码风格?
  • 华为MetaERP PO‑采购接收‑接收检验‑入库 全流程会计核算、SAP / Oracle EBS / Fusion 对比、中国准则审计税法深度分析业务四环节:采购下单 PO(无会计凭证)→采购接
  • 3个技巧让Windows窗口管理效率翻倍:FancyZones深度指南
  • 如何精通DevOps Interview Guide中的数据仓库运维:Snowflake与Redshift实战指南
  • 基于二进制粒子群优化(BPSO)最佳PMU位置(OPP)配置研究附Matlab代码
  • 如何用纯前端技术实现专业级音乐扒谱:noteDigger的技术架构与应用实践
  • Tendermint-rs核心组件解析:Light Client如何保障区块链数据安全?
  • 广州各区初三上学期开学考和分班考试卷及答案解析
  • 技术美术与着色器开发的实战挑战:从视觉魔法到工程实现的技术剖析
  • 如何用OpenCore Legacy Patcher快速修复老旧Mac网络问题:5步完整指南
  • Swift 6高级编程技巧:泛型与元编程实战指南
  • N_m3u8DL-RE解密:三分钟掌握现代流媒体下载核心技术
  • 打造沉浸式游戏音效:Godot-Mixing-Desk散射功能全解析
  • 终极指南:无需VR设备畅玩《半条命:Alyx》的HLA-NoVR模组
  • GTA IV完整版终极修复指南:如何让经典游戏在现代PC上完美运行
  • 革命性3D感知技术:LiDAR-MOS如何通过序列数据提升移动目标分割精度?
  • Android RecyclerView Sample开发指南:从环境搭建到代码实现
  • 缓存友好的数据结构设计原则与实现案例7
  • PhysicsExamples2D性能优化技巧:让你的2D物理场景运行如飞
  • 5分钟解锁macOS隐藏技能:用Whisky让Apple Silicon Mac运行Windows应用
  • Energy Vault宣布达成战略协议,将携手采用Caterpillar发电机组的领先发电EPC承包商,为超大规模AI数据中心部署1.25吉瓦一体化电力基础设施
  • Pixelle-Video AI全自动短视频引擎:零基础3分钟制作专业视频的终极指南
  • 性能对比:convnextv2_base.fcmae_ft_in22k_in1k与178个主流模型的关键指标深度测评
  • 怎么证明你的Agent变强了?3步搭建可量化的性能评估体系,面试汇报都用得上
  • SymPy与EinsteinPy结合:符号化推导爱因斯坦场方程的终极技巧