首页/文章/javascript

闭包和内存泄漏,这次我终于答上来了

2024-10-10
10064 分钟
...

正文

要了解闭包,我们需要先了解词法作用域和作用域链。

词法作用域

在编程语言中,变量和函数的作用域是由其在源代码中的位置决定的。也就是说,在变量和函数被声明的时候其作用域已经确定了。当在函数中访问外部变量时,不论函数运行在哪里,访问的变量始终是其作用域中的那个变量。

let name = 'Agan'
export function getName(){
    console.log(name)
}

例如,我们在这里声明了name 变量和getName 函数,在函数中打印该变量,再将函数导出。如果我们在其他地方引入getName 并运行,则依旧会打印Agan。听起来似乎和我们平时开发时的行为是一致的?没错,在开发中正是一直遵循这套规则。

作用域链

作用域链(Scope Chain)是JavaScript 中用于解析变量的机制。它由多个作用域组成,形成一个链条,确保在查找变量时能够依次访问外部作用域中的变量。

工作原理
  1. 当前作用域:当代码执行时,JavaScript 首先在当前作用域中查找变量。
  2. 外部作用域:如果在当前作用域中找不到该变量,JavaScript 会向外层作用域(即父作用域)查找,知道找到变量或到达全局作用域为止。
  3. 如果在所有作用域中都找不到该变量,JavaScript 会抛出一个错误,表示该变量未定义。

示例:

let globalVar = 'I am global';
function outerFunction() {
    let outerVar = 'I am outer';
    function innerFunction() {
        let innerVar = 'I am inner';
        console.log(innerVar);   
        console.log(outerVar);    
        console.log(globalVar);   
    }
    innerFunction();
}
outerFunction();

闭包

在了解词法作用域和作用域链后,我们可以尝试总结一下闭包的概念。
在嵌套函数中,内部函数可以访问外部函数的变量,就形成了闭包。如果可以通过其他方法调用该内部函数,即使外部函数执行完毕也可以访问到闭包空间中的变量。

举个栗子🌰:

function person() {
    const name = 'Agan'
    function getName() {
        console.log(name)
    }
    return getName
}
let print = person()
print()

我们将person 函数执行,并赋值给print,再执行print 函数,也就是在外部执行getName 函数,它将会打印'Agan'
这样的话,即使外部函数执行完毕,闭包空间中变量仍然可以被访问到。

内存泄漏

关于闭包的应用场景在这里就不介绍了。我们来看看闭包引起的内存泄漏问题,这也是经常会伴随着闭包的问题。
在上面的代码中,我们随时可以通过在外部调用print 函数访问到name 变量,这就意味着这块内存空间始终被name占据,即使我们不使用,也不会被释放,这就造成了内存泄漏。

如何解决呢?
我们只需要在print 函数使用完毕后,并且确定不再需要使用的时候,将print 赋值为null 就好,即print = null

原理:
这么做是因为在js 的垃圾回收机制中,当一个变量无法被直接或间接访问到的时候,在下一次进行垃圾回收时,将会回收这个变量。
上面代码中,在person 函数执行完毕后,我们只能通过print 函数间接访问到name 变量,当我们将print 赋值为null 后,name 变量将永远无法被访问到,于是它将会被垃圾回收器处理。

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
javascript2025-09-18
JavaScript 的多线程能力:Worker
如果你写过一些计算量稍大的 JavaScript 代码,比如图像处理、大量数据排序或者复杂的算法,你几乎肯定遇到过浏览器“卡死”的现象。点击页面没反应,动画也停了,就像整个世界都静止了。 这就是主线程被阻塞的典型后果。因为主线程既要负责执行 JavaScript,又要负责渲染页面、响应用户操作,一旦它被繁重的计算任务占满,就无暇顾及其他,用户体验便直线下降。 这个问题的根源,正是“主线程是单线程的”。那么,如何解决呢? 答案很简单:把这...
学习面试
javascript2025-09-10
手把手教你实现一个EventEmitter
你是不是也遇到过这种情况:代码里各种事件监听和触发,回调函数套了一层又一层,最后自己都理不清哪个事件先触发、哪个后触发了? 别担心,今天我就带你从零开始实现一个自己的事件触发器(EventEmitter),让你彻底掌握事件驱动的精髓,代码清晰度直接提升200%! 为什么要懂EventEmitter? 先说说为什么我们要关心这个。Node.js的核心就是事件驱动,比如文件读写、网络请求,都是通过事件来处理的。不会EventEmitter,...
学习面试
javascript2025-08-09
Web Worker 多线程魔法
你是否曾遇到过网页因为复杂计算而卡顿的情况?今天,让我们一起探索Web Worker的神奇世界,看看如何用多线程技术解决性能瓶颈,并实现流畅的图片压缩功能! 为什么需要Web Worker? 想象一下,你在一个繁忙的餐厅里,只有一位服务员(主线程)负责点餐、上菜、结账所有工作。当客人突然暴增时,这位服务员就会手忙脚乱,其他客人只能干等着… 这就是JavaScript的现状—— 单线程 !所有任务都在一个线程中排队执行,一旦遇到复杂计算(...
学习面试
javascript2025-08-07
🌐AST抽象语法树
AST 是前端开发中一个非常重要的概念,它就像是代码的“编译器”,能够帮助我们理解和操作代码。本文将深入探讨 AST 的原理和应用,带你从零开始了解 AST。 一、什么是 AST? AST(抽象语法树)是一种树形结构,用于表示程序代码的语法结构。它将代码分解为一系列的节点,每个节点代表代码中的一个元素,如变量、函数、表达式等。AST 是编译器和解释器的基础,也是代码分析和转换的关键工具。 1\. AST 的结构 AST 的结构通常包括以...
学习面试
javascript2025-08-05
打字机效果是如何实现的
分析效果 使用 AI 对话的时候,文字是一个字一个字蹦出来的?它不是等所有内容生成完毕后一次性展示,而是以一种流畅、实时的“打字机”效果呈现,这种体验的背后,隐藏着一种高效的Web技术。它就是 服务器推送事件(Server Sent Events,简称 SSE) 什么是 SSE 从根本上说,SSE 是一种允许服务器 单向 、 持续 地向客户端(浏览器)推送数据的Web技术。 可以把它想象成一个 电台广播 : 服务器 是广播电台,持续不断...
学习面试
javascript2025-08-04
Promise 从原理到实现
前言 是 异步编程的核心概念之一,它为我们提供了一种更优雅的方式来处理异步操作。 本文将全面剖析 的工作原理,并逐步实现一个符合 规范的 。 正文 一、Promise 基础概念 代表一个异步操作的最终完成(或失败)及其结果值。它有 种状态: 1. pending : ,既不是成功,也不是失败状态 2. fulfilled :意味着操作 完成 3. rejected :意味着操作 状态一旦改变就不能再变,只能从 变为 ,或从 变为 。 二...
学习面试

评论

请登录后发表评论

去登录
加载评论中...

目录