前面的话

  本文从执行环境的角度来分析闭包,先用一张图开宗明义,然后根据图示内容对代码进行逐行说明,试图对闭包进行更直观的解释

图示

说明

  下面按照代码执行流的顺序对该图示进行详细说明

function foo(){var a = 2;function bar(){console.log(a);}return bar;
}
var baz = foo();
baz();

  【1】代码执行流进入全局执行环境,并对全局执行环境中的代码进行声明提升(hoisting)

  【2】执行流执行第9行代码var baz = foo();,调用foo()函数,此时执行流进入foo()函数执行环境中,对该执行环境中的代码进行声明提升过程。此时执行环境栈中存在两个执行环境,foo()函数为当前执行流所在执行环境

  【3】执行流执行第2行代码var a = 2;,对a进行LHS查询,给a赋值2

  【4】执行流执行第7行代码return bar;,将bar()函数作为返回值返回。按理说,这时foo()函数已经执行完毕,应该销毁其执行环境,等待垃圾回收。但因为其返回值是bar函数。bar函数中存在自由变量a,需要通过作用域链到foo()函数的执行环境中找到变量a的值,所以虽然foo函数的执行环境被销毁了,但其变量对象不能被销毁,只是从活动状态变成非活动状态;而全局执行环境的变量对象则变成活动状态;执行流继续执行第9行代码var baz = foo();,把foo()函数的返回值bar函数赋值给baz

  【5】执行流执行第10行代码baz();,通过在全局执行环境中查找baz的值,baz保存着foo()函数的返回值bar。所以这时执行baz(),会调用bar()函数,此时执行流进入bar()函数执行环境中,对该执行环境中的代码进行声明提升过程。此时执行环境栈中存在三个执行环境,bar()函数为当前执行流所在执行环境

  在声明提升的过程中,由于a是个自由变量,需要通过bar()函数的作用域链bar() -> foo() -> 全局作用域进行查找,最终在foo()函数中也就是代码第2行找到var a = 2;,然后在foo()函数的执行环境中找到a的值是2,所以给a赋值2

  【6】执行流执行第5行代码console.log(a);,调用内部对象console,并从console对象中log方法,将a作为参数传递进入。从bar()函数的执行环境中找到a的值是2,所以,最终在控制台显示2

  【7】执行流执行第6行代码},bar()的执行环境被弹出执行环境栈,并被销毁,等待垃圾回收,控制权交还给全局执行环境

  【8】当页面关闭时,所有的执行环境都被销毁

总结

  从上述说明的第5步可以看出,由于闭包bar()函数的原因,虽然foo()函数的执行环境销毁了,但其变量对象一直存在于内存中,就是为了能够使得调用bar()函数时,可以通过作用域链访问到父函数foo(),并得到其变量对象中储存的变量值。直到页面关闭,foo()函数的变量对象才会和全局的变量对象一起被销毁,从而释放内存空间

  由于闭包占用内存空间,所以要谨慎使用闭包。尽量在使用完闭包后,及时解除引用,以便更早释放内存

//通过将baz置为null,解除引用
function foo(){var a = 2;function bar(){console.log(a);//2
    }return bar;
}
var baz = foo();
baz();
baz = null;
/*后续代码*/

深入理解闭包系列第二篇——从执行环境角度看闭包相关推荐

  1. 深入理解定时器系列第二篇——被誉为神器的requestAnimationFrame

    前面的话 与setTimeout和setInterval不同,requestAnimationFrame不需要设置时间间隔.这有什么好处呢?为什么requestAnimationFrame被称为神器呢 ...

  2. 深入理解javascript函数系列第二篇——函数参数

    前面的话 javascript函数的参数与大多数其他语言的函数的参数有所不同.函数不介意传递进来多少个参数,也不在乎传进来的参数是什么数据类型,甚至可以不传参数.本文是深入理解javascript函数 ...

  3. 前端工程师技能之photoshop巧用系列第二篇——测量篇

    前端工程师使用photoshop进行的大量工作实际上是测量.本文是photoshop巧用系列第二篇--测量篇 测量信息 在网页制作中需要使用photoshop测量的信息分为两类,分别是尺寸信息和颜色信 ...

  4. 焱老师带你学习MYSQL系列 第二篇 (MYSQL 数据结构)

    相关系列链接 焱老师带你学习MYSQL系列 第六篇 (MYSQL是如何实现锁的) 焱老师带你学习MYSQL系列 第五篇 (MYSQL事务隔离级别是如何实现的) 焱老师带你学习MYSQL系列 第四篇 ( ...

  5. [游戏开发]Python打表工具系列 [第二篇] [打表流程描简述]

    [上一篇链接] [游戏开发]Python打表工具系列 [第一篇][IDE开发环境部署] VSCode Python环境调试_Little丶Seven的博客-CSDN博客 [前言] 第二篇文章是对流程的 ...

  6. js执行环境作用域和闭包_JavaScript中执行上下文,提升,作用域和闭包的终极指南

    js执行环境作用域和闭包 It may seem surprising, but in my opinion the most important and fundamental concept to ...

  7. 阿里出品移动研发“神器” 阿里移动云系列第二篇|“移”步到位:一站式移动应用研发体系...

    摘要:2017杭州云栖大会阿里移动云峰会专场上,阿里巴巴高级技术专家小木带来一站式应用研发体系方面的演讲.本文主要以互联网的应用背景开始谈起,进而阐述了已拥有APP的企业在APP的生命周期中会遇见哪些 ...

  8. SparkContext的初始化(伯篇)——执行环境与元数据清理器

    <深入理解Spark:核心思想与源码分析>一书前言的内容请看链接<深入理解SPARK:核心思想与源码分析>一书正式出版上市 <深入理解Spark:核心思想与源码分析> ...

  9. 带有执行环境的函数 - 闭包

    学习 JavaScript 的同学都会面对闭包,总觉得很难,你会看到闭包各种版本的定义,即使把闭包的概念背会也不一定的能够理解闭包.想要掌握闭包,需要看很多关于闭包的资料,加上对 JavaScript ...

最新文章

  1. react非常适合入门者学习使用的后台管理框架
  2. awk截取字符命令_Linux运维基础技能: 脚本编程与Linux命令
  3. 移动互联网的创新与91生态
  4. 产品定义到产品推广的思路
  5. 对话中国经济和信息化-万祥军:李玉庭制造企业重整电商
  6. CENTOS7 Python3.7安装pip模块以及pip使用
  7. 一个IT人士的个人经历,给迷失方向的朋友
  8. pta l2-6(树的遍历)
  9. 洛谷 P5742、P5743、P5744 题解
  10. ftp加速传输java_Java FTPClient 大量数据传输的问题(未解决)
  11. Websphere 手动模拟删除应用操作
  12. 年底绝对不能犯的四个职场错误
  13. 基于Monorail的系统功能模块化
  14. u盘无法格式化不在计算机中,u盘被写保护无法格式化怎么办 u盘无法格式化原因及解决...
  15. 计算机管理内存条,win10系统查看电脑内存条型号的方法
  16. 腾讯企业邮箱好还是阿里云企业邮箱好?
  17. 转换、刻录DVD影碟光盘教程
  18. 每周分享第 31 期
  19. IE8跳转谷歌浏览器亲测有效
  20. 大数据未来发展趋势,主要取决于这八个要素

热门文章

  1. Games101现代图形学入门Lecture 3: Transformation知识点总结
  2. 分类算法中的ROC与PR指标
  3. vue中通过第三方代理解决跨域问题
  4. MySQL Hardware--FIO压测
  5. TCL语言笔记:TCL中的String命令
  6. 移动端系列讲解之字体单位
  7. tomcat Server.xml Context配置
  8. 分布式数据库 HBase
  9. javaScript事件(一)事件流
  10. HDU ACM 1181 变形课 (广搜BFS + 动态数组vector)-------第一次使用动态数组vector