1 引言

变量作用域

首先我们先铺垫一个知识点——变量作用域:

变量根据作用域的不同分为两种:全局变量和局部变量。

  1. 函数内部可以使用全局变量。
  2. 函数外部不可以使用局部变量。
  3. 当函数执行完毕,本作用域内的局部变量会销毁。

如果我想在函数外部引用这个函数的局部变量呢?

2 闭包

闭包是什么?

闭包(closure)指有权访问另一个函数作用域中变量的函数。 ----- JavaScript 高级程序设计

闭包有什么用?

1延伸变量作用域范围,读取函数内部的变量

2让这些变量的值始终保持在内存中

简单理解就是 ,一个作用域可以访问另外一个函数内部的局部变量。

闭包案例一

我们来看一个简单的闭包案例。

        function fn1() {
var num = 10;
function fn2() {
console.log(num);
}
fn2();
}
fn1(); //输出结果10

这样的一个函数写法我们已经见过或者用过很多次了,但其实这就是一个闭包的运用。

我们可以用Chrome的调试工具验证一下。



如图(看不清图片的伙伴们可以把图片放大)

在 Scope 选项(Scope 作用域的意思)中,有两个参数(global 全局作用域、local 局部作用域)。我在fn1函数调用的前面(21行)中设置了一个断点,进行单步调试,当执行到 fn2() 时,Scope 里面会多一个 Closure (闭包)参数 ,这就表明产生了闭包。被访问的变量是num,包含num的函数为fn1。

fn2的作用域当中访问到了fn1函数中的num这个局部变量 ,所以此时fn1 就是一个闭包函数(被访问的变量所在的函数就是一个闭包函数)

也有人说,闭包是一种现象,一个作用域访问了另外一个函数中的局部变量,如果有这种现象的产生,就有了闭包的发生。 我觉的这样理解也是没有什么问题的。

闭包案例二

接下来我们来看一个稍微复杂一点点的闭包

        function fn() {
var num = 10;
return function() {
console.log(num);
}
}
var f = fn();
// 上面这步类似于
// var f = function() {
// console.log(num);
// }
f();//输出结果10

在f=fn()这步操作中,执行了num =10 的赋值,并且给f赋值了一个匿名函数,这个函数是fn中return 返回的那个匿名函数,注意此时只是赋值了,并没有调用。

然后在f()中调用了那个匿名函数,此时我们便做到了在 fn() 函数外面访问 fn() 中的局部变量 num 。

$闭包延伸了变量作用域范围,读取了函数内部的变量$

闭包案例三

首先我们看一下这个闭包案例

        var fn  =function(){
var sum = 0
return function(){
sum++
console.log(sum);
}
}
fn()() //1
fn()() //1
//fn()进行sum变量申明并且返回一个匿名函数,第二个()意思是执行这个匿名函数

这里出现了一个小问题,sum为什么没有自增?如果想要实现自增怎么操作?

回答这个问题需要先了解一下js中内存回收机制。(详细内容可以看文章后面的3 Js内存回收机制

我这里直接简单解释一下,执行fn()() 后,fn()()已经执行完毕,没有其他资源在引用fn,此时内存回收机制会认为fn不需要了,就会在内存中释放它。

那如何不被回收呢?

        var fn  =function(){
var sum = 0
return function(){
sum++
console.log(sum);
}
}
fn1=fn()
fn1() //1
fn1() //2
fn1() //3

这种情况下,fn1一直在引用fn(),此时内存就不会被释放,就能实现值的累加。那么问题又来了,这样的函数如果太多,就会造成内存泄漏(内存泄漏、内存溢出的知识点在文章后面4 内存溢出、内存泄漏

内存泄漏了怎么办呢?我们可以手动释放一下

        var fn  =function(){
var sum = 0
return function(){
sum++
console.log(sum);
}
}
fn1=fn()
fn1() //1
fn1() //2
fn1() //3
fn1 = null // fn1的引用fn被手动释放了
fn1=fn() //num再次归零
fn1() //1

3 Js内存回收机制

由于字符串、对象和数组没有固定大小,当他们的大小已知时,才能对他们进行动态的存储分配。JavaScript程序每次创建字符串、数组或对象时,解释器都必须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便他们能够被再用,否则,JavaScript的解释器将会消耗完系统中所有可用的内存,造成系统崩溃。

现在各大浏览器通常用采用的垃圾回收有两种方法:标记清除、引用计数。

1标记清除

这是javascript中最常用的垃圾回收方式。当变量进入执行环境是,就标记这个变量为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到他们。当变量离开环境时,则将其标记为“离开环境”。

2引用计数

引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型赋值给该变量时,则这个值的引用次数就是1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数就减1。当这个引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其所占的内存空间给收回来。这样,垃圾收集器下次再运行时,它就会释放那些引用次数为0的值所占的内存。

4 内存溢出、内存泄漏

内存溢出

内存溢出一般是指执行程序时,程序会向系统申请一定大小的内存,当系统现在的实际内存少于需要的内存时,就会造成内存溢出

内存溢出造成的结果是先前保存的数据会被覆盖或者后来的数据会没地方存

内存泄漏

内存泄漏是指程序执行时,一些变量没有及时释放,一直占用着内存

而这种占用内存的行为就叫做内存泄漏。

作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积。

内存泄漏如果一直堆积,最终会导致内存溢出问题

5 总结

  1. 闭包是什么?

闭包是一个函数 (一个作用域可以访问另外一个函数的局部变量)

  1. 闭包的作用是什么?

1延伸变量作用域范围,读取函数内部的变量

2让这些变量的值始终保持在内存中

JavaScript闭包(内存泄漏、溢出以及内存回收),超直白解析的更多相关文章

  1. JS内存泄漏 和Chrome 内存分析工具简介(摘)

    原文地址:http://web.jobbole.com/88463/ JavaScript 中 4 种常见的内存泄露陷阱   原文:Sebastián Peyrott 译文:伯乐在线专栏作者 - AR ...

  2. Android 性能优化之内存泄漏检测以及内存优化(中)

    https://blog.csdn.net/self_study/article/details/66969064 上篇博客我们写到了 Java/Android 内存的分配以及相关 GC 的详细分析, ...

  3. 内存泄漏 Memory Leaks 内存优化 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  4. 使用valgrind进行内存泄漏和非法内存操作检测

    valgrind是一个强大的工具,最常用的功能是用它来检测内存泄漏和非法内存的使用.要想让valgrind报告的更加细致,请使用-g进行编译. 基本命令如下: $ valgrind --tool=me ...

  5. 使用 Android Studio 检测内存泄漏与解决内存泄漏问题

    本文在腾讯技术推文上 修改 发布. http://wetest.qq.com/lab/view/63.html?from=ads_test2_qqtips&sessionUserType=BF ...

  6. JNI创建共享内存导致JVM terminated的问题解决(segfault,shared memory,内存越界,内存泄漏,共享内存)

    此问题研究了将近一个月,最终发现由于JNI不支持C中创建共享内存而导致虚拟机无法识别这块共享内存,造成内存冲突,最终虚拟机崩溃. 注意:JNI的C部分所使用的内存也是由JVM创建并管理的,所以C创建了 ...

  7. 记录一个问题:macos High Sierra 10.13.6 内核内存泄漏,导致内存满而不得不重启

    kernel_task进程占用内存10g以上,使用中突然提示内存不足,要求杀死工作进程,不得不强按电源键来关机重启. 升级之前,版本大约是macos High Sierra 10.13.4, 系统频繁 ...

  8. 了解 JavaScript 应用程序中的内存泄漏

    简介 当处理 JavaScript 这样的脚本语言时,很容易忘记每个对象.类.字符串.数字和方法都需要分配和保留内存.语言和运行时的垃圾回收器隐藏了内存分配和释放的具体细节. 许多功能无需考虑内存管理 ...

  9. JavaScript内存泄漏知多少?

    垃圾回收解放了我们,它让我们可将精力集中在应用程序逻辑(而不是内存管理)上.但是,垃圾收集并不神奇.了解它的工作原理,以及如何使它保留本应在很久以前释放的内存,就可以实现更快更可靠的应用程序.在本文中 ...

随机推荐

  1. PHP mysqli_rollback() 函数

    关闭自动提交,做一些查询,提交查询,然后回滚当前事务: <?php 高佣联盟 www.cgewang.com // 假定数据库用户名:root,密码:123456,数据库:RUNOOB $con ...

  2. EACCES: permission denied,mkdir … npm install 安装依赖问题解决

    强哥最近在用hugeGraph图库做二次开发的时候,在打包的时遇到前端项目打包失败的问题: cwebp-bin@4.0.0 postinstall /home/hugegraph/my-hugegra ...

  3. linux的PS进程和作业管理(进程调度,杀死进程和进程故障-僵尸进程-内存泄漏)

     Ps进程和作业管理 1.查看进程ps 1.格式 ps   ---查看当前终端下的进程 3种格式: SYSV格式   带 - 符号 BSD格式  不带 - 符号 GNU格式   长选项 2.ps -a ...

  4. Python的基本运用(一)

    1.a**b  表示a的b次方. 2.def something(a,b):  定义函数,注意 python的缩进 . 3.print (a)与print a 的区别,python3中不支持print ...

  5. 使用VMware虚拟机建立Ubuntu与主机win7的文件共享与传输

    1.要想在虚拟机与主机之间建立共享文件夹必须先安装VMware Tools.方法见https://www.cnblogs.com/lsc666js/p/13403919.html. 2.在VMware ...

  6. 【AHOI2009】同类分布 题解(数位DP)

    题目大意:求$[l,r]$中各位数之和能被该数整除的数的个数.$0\leq l\leq r\leq 10^{18}$. ------------------------ 显然数位DP. 搜索时记录$p ...

  7. JavaScript异步编程——Async/Await vs Promise

    兼容性 提醒一下各位,Node 现在从版本 7.6 开始就支持 async/await 了.而就在前几天,Node 8已经正式发布了,你可以放心地使用它. 如果你还没有试过它,这里有一堆带有示例的理由 ...

  8. Zabbix5 Frame 嵌套

    Zabbix5 Frame 嵌套 Zabbix 默认不允许嵌套在其他页面上,通过修改配置允许嵌套 找到 Zabbix 下面的 include/defines.inc.php 文件,末尾有一行 defi ...

  9. 痞子衡嵌入式:了解i.MXRTxxx系列ROM API及其与i.MXRT1xxx系列的差异

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRTxxx系列ROM API设计细节. 痞子衡之前写过两篇文章 <利用i.MXRT1xxx系列ROM提供的FlexSPI ...

  10. Apache Hudi表自动同步至阿里云数据湖分析DLA

    1. 引入 Hudi 0.6.0版本之前只支持将Hudi表同步到Hive或者兼容Hive的MetaStore中,对于云上其他使用与Hive不同SQL语法MetaStore则无法支持,为解决这个问题,近 ...