关于从内存上分析闭包的原理,有基础的可以直接看目录第六条,前面的几个都是基础知识,到处都有,不看也罢

1. 如何产生闭包?

  • 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包

2. 闭包到底是什么?

  • 理解一: 闭包是嵌套的内部函数
  • 理解二: 包含被引用变量(函数)的对象
  • 注意: 闭包存在于嵌套的内部函数中

3. 产生闭包的条件?

  • 函数嵌套
  • 内部函数引用了外部函数的数据(变量/函数)
  • 外部函数执行
    值得注意的是,闭包是放置在内部函数里面的,可以理解为内部函数对象的属性里存了外部函数的变量。
 function fn1 () {var a = 2var b = 'abc'function fn2 () { //执行函数定义就会产生闭包(不用调用内部函数)外部还是要调用的console.log(a)}// fn2()}fn1()

4.常见的两种闭包使用场景

1**.将函数作为它外部函数的返回值**

 // 1. 将函数作为另一个函数的返回值function fn1() {var a = 2function fn2() {a++console.log(a)}return fn2}// fn1()() // 3   //先执行fn1()产生了一个闭包// fn1()() // 3   //先执行fn1()产生了一个闭包  也就是会产生两个闭包var f = fn1()          //fn1()返回的值是指向函数fn2的地址,也就是f存的是fn2的地址!!//而不能理解成f等于fn1函数!!于是f()是直接调用fn2函数!不能理解成fn1()()!!//因为一旦理解成fn1()(),下两行代码就各自都调用了一次外部函数fn1了!f()  //3f()   //4

正常情况下,函数调用结束,其中的变量会死亡,但是这里的a在增加,说明a被存起来了!实际上,在调用fn1的时候,因为先进行变量提升,然后进行函数fn2提升,电脑看到fn2中调用了外部函数的a,就会在fn2中开辟一个空间,用来存放调用的外部函数的变量的值!这便是闭包!

如上图,当执行fn1()时,代码刚进入fn1内部,就生成了闭包,此时a变量仅仅是变量提升了,还没有定义,所以是undefined。而且注意到,这个闭包是放在内部函数fn2里面的!
另外,是调用fn1时才会生成一个闭包,上面的程序,仅仅调用了一次fn1,所以也就只有产生1个闭包,所以两次f()所用的是同一个闭包中存的值,所以才能取到3和4的值。

2.将函数作为实参传递给另一个函数调用

 // 2. 将函数作为实参传递给另一个函数调用function showDelay(msg, time) {setTimeout(function () {alert(msg)}, time)}showDelay('atguigu', 2000)

5.闭包的作用

  1. 使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)
  2. 让函数外部可以操作(读写)到函数内部的数据(变量/函数)

问题:

  1. 函数执行完后, 函数内部声明的局部变量是否还存在?
    一般是不存在, 存在于闭中的变量才可能存在

  2. 在函数外部能直接访问函数内部的局部变量吗?
    不能, 但我们可以通过闭包让外部操作它

6,啥鬼东西啊!?就不信理解不了!!!

一般情况下,在函数fn执行完后,就应该连同它里面的变量一同被销毁,但是在这个例子中,匿名函数作为fn的返回值被赋值给了fn1,这时候相当于fn1=function(){var n = 0 … },并且匿名函数内部引用着fn里的变量num,所以变量num无法被销毁,而变量n是每次被调用时新创建的,所以每次fn1执行完后它就把属于自己的变量连同自己一起销毁,于是乎最后就剩下num,那它存储在哪里呢?这就要看这句代码了:var fn1=fn()。fn1存的是地址,指向原本function(){var n = 0 … }所在的地址,里面就存着变量num的值!所以说,即使函数fn结束执行把自己的相关局部变量都清除了,但是留下了一个地址,指向闭包所在的区域,那个区域里面正是存着这个函数function(){var n = 0 … },且闭包中的num值还在,所以下次可以继续调用。
----------------------------------------------------------------------------我是能看懂的分界线 ------------------------------------------------------
以上都是代码加文字,很乱,我再梳理一下自己的理解。
凌晨两点了啊!!我的觉觉!!!
首先,需要理解的前置知识有:函数的调用栈和函数的编译执行原理、作用域链(这个之前文章说过了)、栈和堆

函数的调用栈和函数的编译执行原理

1.函数执行之前为一段字符串

2.函数通过function构造器构建

3.函数构建完成后存储于堆空间

4.它被调用的时候,函数会被一个内容为空的object对象作为主体对象,将这个函数获取并调并存储至栈空间中执行

5.执行完后,这个调用函数的object对象自动被销毁,所以函数的调用是存储于栈空间的。

这时候,函数fn1还没有执行。接下来执行函数fn1:

在栈空间完成函数的执行后,fn1的执行上下文会被pop出栈销毁,这就能解释为什么js中函数中定义的变量是局部变量了!
然后内存恢复到最初的状态:

闭包的内存执行原理

代码刚运行时,内存是这样的,此时var=fn1()还没有执行

现在执行var fn=fn1()

var fn=fn1()执行完毕后:

注意看上图的fn被赋值了!里面存的是函数fn2的地址了,也就是说,我们可以通过fn在全局调用函数fn2,并且因为fn2是函数fn1的子函数,结合作用域链由内向外查找的知识,我们就可以在全局通过fn2访问fn1中的变量了!
**闭包中的num的值实际上并不是存储在fn2的区域内,而是依旧存储在fn1的区域内!**这点需要明确,因为如果是存在fn2中,在调用fn2时,执行上下文中会出现num=3的赋值语句,每次调用num都会被初始化成3,而且函数结束后还会被销毁,更不会存储变更后的值,这样就和实际不一样,合理的解释就是num的值依旧存储在fn1的区域内!只不过执行fn2时,去fn1的作用域中查找罢了,这样一来,fn2中对num的更改就会存储在fn1中的num上了,这才是为何闭包调用时访问的外部函数变量是最新值得根本原因。
然而执行fn2时的上下文是去fn2中复制一份需要的数据出来执行的,执行完毕之后就销毁,如果仅仅是去fn1的num取值,变更后的num值并不会返回fn1中的num,要达到能存储变更的num,闭包中的num存储的就不应该是num的值,而应该是地址,指向fn1中的num。
如果上面这段话不理解,咱们先接下去执行,待会就懂了。
先把闭包是存储地址的图示画出来;

接下来执行fn:
第一步是把fn函数执行需要的东西从fn中“复制”一份到调用栈中
第二步才是在调用栈中执行这个函数
第三步执行完毕后pop这个函数的上下文。
因为fn2中的num是存地址,直接“复制”到调用栈中,它依旧指向fn1中的num。

执行完毕后:fn1中的num就变成4了!

再执行fn

执行完毕后,fn1中的num就变成5了。

前端小白,以上的东西是根据代码运行的现象自己推理出来的,我也不知道对不对,但是它能解释我目前遇到的种种现象。所以我就先这么理解了,等后续知识水平上来了,再做补充。如果有大佬路过,跪求指点哇!

++++++++++++++++++++++++++++++++++++++++++++++++分割线+++++++++++++++++++++++++++
今天遇到了新的问题,用之前的方法理解闭包在内存中的存在,会产生错误,说明之前对闭包的理解还是不够深刻。
遇到的问题是这样的:

         function Foo(){var i=0return function(){console.log(i++)}}var f1=Foo()var f2=Foo()f1()   //0f1()   //1f1()  //2f2()   //0f2()   //1

按照之前对闭包的理解,这里的f2和f1使用的应该是同一个i值,也就是应该打印3和4

为了解释这种现象,我又在之前的基础添加了一个想法,也就是闭包并不会改变原本Foo函数中的i值,只不过每次调用Foo时,就会在堆空间生成一个“闭包”,来存储当前使用到的变量i.

也就是说,每调用一次Foo,就会在堆空间基于Foo内初始值生成一个新的闭包:

js高级学习笔记-14-从函数运行和内存角度理解闭包相关推荐

  1. 燕十八老师 JS高级学习笔记 之作用域链

    作用域 在JS中,函数嵌套是非常普遍的,在函数嵌套中:对变量是如何寻找的? 答: 首先在函数内寻找,寻找不到,则在外层寻找 ...直到..全局(window)区域. // 例子1var c=5;fun ...

  2. JS高级学习笔记(6)- 事件循环

    参考文章:深入理解JS引擎的执行机制        JavaScript 异步.栈.事件循环.任务队列 我的笔记:ES系列之Promise async 和 await Event Loop 前提 js ...

  3. 数据可视化清新版【chart.js】学习笔记8.0—极地图(Polar Area)

    Polar Area--(极地图) 极地面积图类似于饼图,但每个线段具有相同的角度 - 线段的半径因值而异.当我们想要显示类似于饼图的比较数据,同时也要显示上下文的值的范围时通常使用这种类型的图表. ...

  4. node.js学习笔记14—微型社交网站

    node.js学习笔记14-微型社交网站 1.功能分析 微博是以用户为中心,因此需要有注册和登录功能. 微博最核心的功能是信息的发表,这个功能包括许多方面,包括:数据库访问,前端显示等. 一个完整的微 ...

  5. 尚学堂JAVA高级学习笔记_1/2

    尚学堂JAVA高级学习笔记 文章目录 尚学堂JAVA高级学习笔记 写在前面 第1章 手写webserver 1. 灵魂反射 2. 高效解析xml 3. 解析webxml 4. 反射webxml 5. ...

  6. js/jquery学习笔记

    javascript简介 JavaScript是一种基于对象和事件驱动并具有相对安全性的客户端脚本语言. 不同于服务器端脚本语言,例如PHP与ASP,JavaScript是客户端脚本语言,也就是说Ja ...

  7. JS逆向学习笔记 - 持续更新中

    JS逆向学习笔记 寻找深圳爬虫工作,微信:cjh-18888 文章目录 JS逆向学习笔记 一. JS Hook 1. JS HOOK 原理和作用 原理:替换原来的方法. (好像写了句废话) 作用: 可 ...

  8. jquery学习笔记及常用函数封装

    二.JQuery 学习笔记及常用函数封装 https://download.csdn.net/download/weixin_42530002/13087988 1.JQuery入门 (1).css选 ...

  9. JAVA基础与高级学习笔记

    JAVA基础与高级学习笔记 /记录java基础与高级,除了较简单的内容,没有必要记录的没有记录外,其余的都记录了/ java初学者看这一篇就够了,全文 6万+ 字. JAVA基础 java会出现内存溢 ...

  10. 【带着canvas去流浪(11)】Three.js入门学习笔记

    [摘要] three.js 入门学习笔记 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 资料推荐及建议 1.官方文档 很详细,但是API部分单独 ...

最新文章

  1. Cocos Creator快速开通联网服务教程
  2. John the Ripper
  3. python【Matlibplot绘图库】-认识Matploblib
  4. Android 手机影音 开发过程记录(六)
  5. android上使用蓝牙设备进行语音输入
  6. 计算机设备抽象,计算机系统原理(三) 金字塔形的存储设备、操作系统的抽象概念...
  7. 互动场景下的低延迟编码技术
  8. python画两条曲线_查找在matplotlib中绘制的两条曲线之间的区域(在区域之间填充)...
  9. GBT19056精要
  10. 【React Native开发】React Native控件之DrawerLayoutAndroid抽屉导航切换组件解说(13)
  11. opencv中滚动条操作
  12. SVN 小乌龟(TortoiseSVN)本地文件更新报错Another process is blocking the working copy database 解决方法
  13. excel打开超链接不使用浏览器,使用默认图片浏览软件
  14. LOLBox多玩饭盒Android源码
  15. 连接局域网及共享打印机提示操作无法完成(0x00000709)
  16. 用Burg法估计AR模型并绘制功率谱曲线的python实现
  17. mysql 错误码1236_【MySql】MySQL Replication Fatal Error 1236
  18. 计算机图形学基础徐文鹏知识点,计算机图形学基础(OpenGL版)
  19. java面向对象oop阶段总结
  20. 【Hard to Park】Estimating Parking Difficulty at Scale

热门文章

  1. 常问的数据结构与算法
  2. dao获取到mysql存储函数_GreenDao3.0使用
  3. 如何正视自己的劣势?面试!
  4. 将React Native集成至Android原生应用
  5. Linux下安装、配置、授权、调优Mysql
  6. Windows Phone开发(25):启动器与选择器之WebBrowserTask 转:http://blog.csdn.net/tcjiaan/article/details/7404770...
  7. PHP的图片等比缩放
  8. docker网络、bridge、host
  9. 为什么我创建了一个计算机用户名 再打开计算机时 我打不开以前的文件,电脑中office文件无法打开的三种解决方法...
  10. 【Spring-AOP】源码分析汇总