闭包

129 阅读6分钟

Hang on, the tenderness of the world is waiting for you.
坚持一下,这世间的温柔在等你啊。

u=615606299,249343212&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto.webp

为什么要认识闭包

只有正常理解闭包后才能避免写出性能问题以及内存泄漏问题

闭包是怎么形成的

形式为函数嵌套函数,内部函数被return。但不是所有的函数嵌套函数都能形成闭包,如果函数内部不调用外部的变量,就不会形成闭包。但是如果调用了外部变量,那么就会形成闭包。

含义

解释有很多种,可选择自己容易理解的。

  • 官方解释为:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数)
  • 红宝书:闭包是指有权访问另一个函数作用域中的变量的函数
  • 其他解释为:闭包就是能够读取其他函数内部变量的函数,通俗的讲就是函数a的内部函数b被函数a外部的一个变量引用的时候,就创建了一个闭包。
  • 简单的说,闭包就是外部函数Fa中的变量被内部函数Fb引用,从而产生了闭包。
  • 闭包(Closure)是指一个函数可以访问并操作其外部作用域中的变量。

什么时候会用到闭包

  • 定时器
  • 函数封装
  • 模块中定义变量(当我们需要在模块中定义一些变量并希望这些变量一直保存在内存中但又不会“污染”全局的变量时,就可以用闭包来定义这个模块)

作用

  • 保护——私有变量和外界没有必然联系
  • 保存——形成不销毁的栈内存,里面的私有变量等信息保存下来了

原理

当一个函数嵌套在另一个函数内部时,内部函数可以访问外部函数的变量,这种能力就是闭包。

作用域

  • 全局
  • 函数
  • 块级——解决var的问题,新增let和const,一般有{}都算做时一个块级作用域

作用域链

作用域里面嵌套作用域,就形成了作用域链,外部作用域无法访问内部的作用域

优点

  • 封装
  • 减少全局变量
  • 减少传递函数的参数量
  • 有效地实现前端组件化和模块化,提高代码的可重用性和可维护性
  • 实现异步编程,使得前端应用可以更加灵活和响应式(定时器和事件监听)

闭包可能导致的问题

性能——使用闭包会占用内存资源

内存泄漏

是什么:内存泄漏时因为不再用到的内存没有及时释放——对于持续运行的服务进行,必须及时释放不再用到的内存。否则,内存占用也越来越高,轻则影响系统性能,重则导致进程崩溃
常见的几种内存泄漏
  • 闭包
  • 缓存
  • 定时器
  • 事件监听
  • 全局变量
  • dom清空或删除时,事件未清除
解决办法
  • 手动清除——在闭包的最后,手动将外部变量和函数置为null,以释放内存
  • 使用IIFE——可以使用立即执行函数表达式来创建一个独立的作用域,并在作用域结束时自动清除变量和函数
  • 避免循环引用——当闭包和外部对象之间存在循环引用时,需要特别注意变量的清除。可以在外部对象中定义一个方法来清除闭包变量。
如何排查——借助Chrome Devtool中的Performce+Memory面板

简单写个闭包

function a() { var i = 0; function b() { alert(++i) } return b; } var c = a(); c();//外部的变量

立即执行函数

含义:声明后便直接执行的安徽念书

作用

  • 封装
  • 防止变量全局污染
  • 保证内部变量的安全

和闭包的关系

解决闭包的一种方法——加立即执行函数,使得匿名函数内的变量i与外部函数的变量i的引用关系断掉

GC垃圾回收机制

为什么:为了解决内存溢出导致程序崩溃的问题

含义

是指在程序运行过程中,自动检测并清除不再使用的垃圾对象的一种机制——JS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收

目的

解决内存管理的问题,可以有效地管理内存资源,提高程序的可靠性和性能

方式

标记清除法——现代浏览器通用的
标记-从根节点(全局变量)遍历为每个可以访问到的对象都打上一个标记,表示该对象可达(达就是使用)
清除——那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收
引用计数法
追踪每个变量被引用的次数,当引用数为0将可以被回收掉

内存的生命周期

  • 内存分配——昂我们声明变量、函数和对象的时候,系统会自动帮我们分配内存
  • 内存使用——即读写内存,也就是使用变量、函数等
  • 内存回收——使用完毕,由垃圾回收自动回收不再使用的内存
说明
  • 全局变量一般不会回收——关闭页面回收
  • 一般情况下,局部变脸的值不用了,会被自动回收掉

算法

新生代
是什么
  • 对象存活时间较短,新生对象或只经过一次垃圾回收的对象,例如局部变量
  • 简单来说就是Svavenge算法——把V8堆空间划半分(对象区域和空闲区域)
老生代
是什么
  • 对象存活时间较长,经历过一次或多次垃圾回收的对象,例如全局变量
  • 简单来说就是标记清除Mark-Swppe和标记整理Mark-Compact

内存溢出out of memory

是什么

就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出

和内存泄漏的联系

一次内存泄漏可能对程序运行没有明显的影响,多次内存泄漏最终会导致内存溢出
举个例子:比如总内存大小是100MB,次程序运行结束有,有10MB没有释放,当前可内存还有90MB,程序还可以运行。但是多次运行后,可用内存最终为0,没有可以内存或内存不足时,程序在下一次运行时,会因为内存不足,而出现内存溢出解决办法

解决办法

  • 安装两个npm包 npm install cross-env increase- memory- limit
  • 在package.json中添加fix-memory-limit命令"scripts": {"serve": "vue-cli-service serve","'build": "vue-cli-service build","fix-
    memory-limit": "cross- envLIMIT=4096 increase-memory-limit"},
  • 如果报错“node -max-old-space- size=4096不是内部或外部命令 在项目的node_ modules.bin下面找到所有的* .cmd文件,在ENDLOCAL语句的上边一行,修改"% prog%"改为% prog%,去掉双引号
  • LIMIT是你想分配的内存大小,然后执行npm run fix-memory-limit
  • 执行npm run serve重新启动项目,就不会再内存溢出了