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 fclose() 函数

    定义和用法 fclose() 函数关闭打开的文件. 该函数如果成功则返回 TRUE,如果失败则返回 FALSE. 语法 fclose(file) 参数 描述 file 必需.规定要关闭的文件. 实例 ...

  2. PHP strrchr() 函数

    实例 搜索 "world" 在字符串中的位置,并返回从该位置到字符串结尾的所有字符: <?php高佣联盟 www.cgewang.comecho strrchr(" ...

  3. PHP stripcslashes() 函数

    实例 删除 "World!" 前面的反斜杠: <?php高佣联盟 www.cgewang.comecho stripslashes("Hello World!&qu ...

  4. mysql中走与不走索引的情况汇集(待全量实验)

    说明 在MySQL中,并不是你建立了索引,并且你在SQL中使用到了该列,MySQL就肯定会使用到那些索引的,有一些情况很可能在你不知不觉中,你就“成功的避开了”MySQL的所有索引. 索引列参与计算 ...

  5. python5.3二进制文件的读写

    fh=open(r"C:\1.png","rb")#转换成二进制数据data=fh.read()#对二进制数据进行读取 fh1=open(r"C:\2 ...

  6. List集合遍历时修改元素出现并发修改异常总结

    什么是并发修改异常: 当我们在遍历实现了collection接口与iterator接口的集合时(List.Set.Map), 我们可以通过遍历索引也可以通过迭代器进行遍历.在我们使用迭代器进行遍历集合 ...

  7. Jenkins持续集成(下)-Jenkins部署Asp.Net网站自动发布

    环境:Windows 2008 R2.Jenkins2.235.1.Visual Studio 2017: 概要 前面写过一篇文章,<自动发布-asp.net自动发布.IIS站点自动发布(集成S ...

  8. .NET 跨平台框架Avalonia UI: 填坑指北(一):熟悉UI操作

    Avalonia 是一个跨平台的 .NET UI 框架,支持 Windows.Linux.Mac OSX... (以及Android  IOS soon..) 本篇主要介绍Avalonia开发过程和L ...

  9. 性能分析(5)- 软中断导致 CPU 使用率过高的案例

    性能分析小案例系列,可以通过下面链接查看哦 https://www.cnblogs.com/poloyy/category/1814570.html 前言 软中断基本原理,可参考这篇博客:https: ...

  10. ES读写流程

    简述ES的写流程,GET读取数据流程和Search搜索数据流程. ES的读写流程主要是协调节点,主分片节点.副分片节点间的相互协调. ES的读取分为GET和Search两种操作.GET根据文档id从正 ...