之前写过一篇JavaScript 闭包究竟是什么的文章理解闭包,觉得写得很清晰,可以简单理解闭包产生原因,但看评论都在说了解了作用域链和活动对象才能真正理解闭包,起初不以为然,后来在跟公司同事交流的时候发现作用域和执行环境确实很重要,又很基础,对理解JavaScript闭包很有帮助,所以在写一篇对作用域和执行环境的理解。

作用域

作用域就是变量和函数的可访问范围,控制着变量和函数的可见性与生命周期,在JavaScript中变量的作用域有全局作用域和局部作用域。

单纯的JavaScript作用域还是很好理解的,在一些类C编程语言中花括号内的每一段代码都有各自的作用域,而且变量在声明它们的代码段外是不可见的,称之为块级的作用域,JavaScript容易让初学者误会的地方也在于此,JavaScript并没有块及的作用域,只有函数级作用域:变量在声明它们的函数体及其子函数内是可见的。

变量没有在函数内声明或者声明的时候没有带var就是全局变量,拥有全局作用域,window对象的所有属性拥有全局作用域;在代码任何地方都可以访问,函数内部声明并且以var修饰的变量就是局部变量,只能在函数体内使用,函数的参数虽然没有使用var但仍然是局部变量。

var a=3; //全局变量function fn(b){ //局部变量c=2; //全局变量var d=5; //局部变量function subFn(){var e=d; //父函数的局部变量对子函数可见for(var i=0;i<3;i++){console.write(i);}alert(i);//3, 在for循环内声明,循环外function内仍然可见,没有块作用域
                }}alert(c); //在function内声明但不带var修饰,仍然是全局变量

只要是理解了JavaScript没有块作用域,简单的JavaScript作用域很好理解,还有一点儿容易让初学者迷惑的地方是JavaScript变量可函数的与解析或者声明提前,好多种叫法但说的是一件事情,JavaScript虽然是解释执行,但也不是按部就班逐句解释执行的,在真正解释执行之前,JavaScript解释器会预解析代码,将变量、函数声明部分提前解释,这就意味着我们可以在function声明语句之前调用function,这多数人习以为常,但是对于变量的与解析乍一看会很奇怪

console.log(a); //undefinedvar a=3;console.log(a); //3console.log(b); //Uncaught ReferenceError: b is not defined

上面代码在执行前var a=3; 的声明部分就已经得到预解析(但是不会执行赋值语句),所以第一次的时候会是undefined而不会报错,执行过赋值语句后会得到3,上段代码去掉最后一句和下面代码是一样的效果。

var a;console.log(a); //undefineda=3;console.log(a); //3

然而

如果只是这样那么JavaScript作用域问题就很简单了,然而由于函数子函数导致的问题使作用域不止这样简单。大人物登场——执行环境或者说运行期上下文(好土鳖):执行环境(execution context)定义了变量或函数有权访问的其它数据,决定了它们的各自行为。每个执行环境都有一个与之关联的变量对象(variable object, VO),执行环境中定义的所有变量和函数都会保存在这个对象中,解析器在处理数据的时候就会访问这个内部对象。

全局执行环境是最外层的一个执行环境,在web浏览器中全局执行环境是window对象,因此所有全局变量和函数都是作为window对象的属性和放大创建的。每个函数都有自己的执行环境,当执行流进入一个函数的时候,函数的环境会被推入一个函数栈中,而在函数执行完毕后执行环境出栈并被销毁,保存在其中的所有变量和函数定义随之销毁,控制权返回到之前的执行环境中,全局的执行环境在应用程序退出(浏览器关闭)才会被销毁。

作用域链

当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain,不简称sc)来保证对执行环境有权访问的变量和函数的有序访问。作用域第一个对象始终是当前执行代码所在环境的变量对象(VO)

function a(x,y){var b=x+y;return b;}

在函数a创建的时候它的作用域链填入全局对象,全局对象中有所有全局变量

如果执行环境是函数,那么将其活动对象(activation object, AO)作为作用域链第一个对象,第二个对象是包含环境,下一个是包含环境的包含环境。。。。。

function a(x,y){var b=x+y;return b;}var tatal=a(5,10);

这时候 var total=a(5,10);语句的作用域链如下

在函数运行过程中标识符的解析是沿着作用域链一级一级搜索的过程,从第一个对象开始,逐级向后回溯,直到找到同名标识符为止,找到后不再继续遍历,找不到就报错。

再来看看闭包

之前博客曾经总结道:只要存在调用内部函数的可能,JavaScript就需要保留被引用的函数。而且JavaScript运行时需要跟踪引用这个内部函数的所有变量,直到最后一个变量废弃,JavaScript的垃圾收集器才能释放相应的内存空间。回头再看看好理解了很多,父函数定义的变量在子函数的作用域链中,子函数没有被销毁,其作用域链中所有变量和函数就会被维护,不会被销毁。

for(var i=0;i<elements.length;i++){elements[i].onclick=function(){alert(i);}}

这是上篇博客提到过的经典错误,每次element点击alert都是length,这段代码中为element绑定的click事件处理程序的作用域链是这样的

由于内部函数(click事件处理程序时刻有调用可能),所以其作用域链不能被销毁(更别说本例中i在全局作用域中,只能页面卸载是销毁),i的值一直保持for循环执行完后的length值,所以每次触发onclick的时候才会alert length。

for(var i=0;i<elements.length;i++){(function(n){elements[n].onclick=function(){alert(n);}})(i);}

为什么这样就行了呢,这时候onclick引用的变量变成了n,而由于立即执行函数的原因,每个onclick函数在作用域链中分别保持着对应的n(0~length-1),这时候就可以了。

最后

其实理解了执行环境和作用域链后,闭包翻了变成显而易见的东西,但是也不能滥用闭包,从上面例子可以看出,闭包会使子函数保持其作用域链的所有变量及函数与内存中,内存消耗很大,在使用的时候尽量销毁父函数不再使用的变量。

转载于:https://www.cnblogs.com/dolphinX/p/3280876.html

JavaScript作用域链相关推荐

  1. 对javascript作用域链的理解

    这几天学习了一下javascript作用域链,感到这个挺重要的,所以写一篇文章分享一下: 1. 作用域链: 当代码在一个环境中执行时,会创建由变量对象构成的一个作用域链.作用域链的用途是:保证对执行环 ...

  2. 擒贼先擒王,简单谈一下JavaScript作用域链(Scope Chain)

    前言 我们都知道一个执行上下文的数据(变量.函数声明和函数的形参)作为属性存储在变量对象中,同时我们也应该知道变量对象在每次进入上下文时创建并填入初始值,值的更新出现在代码执行阶段.那么咱们专门讨论与 ...

  3. javascript作用域链与原型链有联系吗?

    一般来说,作用域链是针对变量的,js里面大的范围上来说,只有两种作用域,全局作用域和函数内部作用域,如果函数1里面又定义了函数2(一般都是匿名函数), 那么就有了这么一个作用域链全局作用域==> ...

  4. JavaScript作用域和作用域链详解

    JavaScript作用域链详解 一.JavaScript作用域 JavaScript作用域是什么? 作用域范围 二.JavaScript作用域链 作用域与执行上下文 总结 一.JavaScript作 ...

  5. JavaScript之作用域链

    本文共 1200 字,读完只需 4 分钟 概述 JavaScript 中的可执行代码有其执行上下文,在执行上下文中,有三个重要的元素: 变量对象(variable object) 作用域链(scope ...

  6. JavaScript执行环境及作用域(一)——执行环境栈和作用域链机制

    2019独角兽企业重金招聘Python工程师标准>>> 执行环境是JavaScript中最为重要的一个概念,每个执行环境都有一个与之关联的变量对象,执行环境中所有的变量和函数都保存在 ...

  7. JavaScript 开发进阶:理解 JavaScript 作用域和作用域链(上)

    作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理.今天这篇文章对JavaScript作用域和作用域链作简单的介绍,希望 ...

  8. Javascript的作用域,作用域链,闭包

    1,作用域和作用域链概念 作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期.在JavaScript中,变量的作用域有全局作用域和局部作用域两种. 1.1 全局作用域,在代码 ...

  9. Javascript 进阶 作用域 作用域链

    一直觉得Js很强大,由于长期不写js代码,最近刚好温故温故. 1.Javascript没有代码块作用域的概念,局部作用域是针对函数来说的. [javascript] view plaincopy fu ...

最新文章

  1. 腾讯全力支持鸿蒙,腾讯宣布大力发展车联网,或与华为鸿蒙强强联合!
  2. 切歌请按3或4!我把树莓派装进了旋转电话里,现在它成了一部MP3
  3. 谷歌AI智商达苹果Siri两倍 但不及6岁儿童
  4. 关于asp.net中partial,asp.net编译笔记
  5. 如何讲页面打入jar包中_如何把我的Java程序变成exe文件?
  6. 国家开放大学2021春1295社会心理学(本)题目
  7. java 窗口扩大,JavaScript实现拉框放大
  8. 【算法】【JAVA】冒泡排序
  9. C++统计博客园写过的代码行数
  10. 树莓派python3导入cv2
  11. java中求某个时间跳过节假日和周末
  12. ChatGPT近视眼镜购买指南:防雾防尘、舒适度与价格平衡的完美选择
  13. java virtual的作用_JVM是java虚拟机(JVM Java Virtual Machine),java程序需要运行在虚
  14. 如何理解特征函数characteristic function
  15. 可跨页字符串:使用Spans设置文本样式
  16. google翻译不能用后chrome浏览器如何翻译网页
  17. 苹果电脑错误代码43_苹果最臭名昭著的代码错误
  18. C#跑马灯抽签小程序
  19. 公民身份号码是一种由18位数字组成的特征组合码,其排列顺序从左至右依次为:6位数字地址码、8位数字出生日期码,3位数字顺序码和1位数字校验码(校验码若为10则用字符X来表示)。编写程序从键盘输入一个
  20. debian使用iso镜像搭建源

热门文章

  1. Unity的学习笔记(鼠标移动控制视角移动)
  2. python学习之matplotlib绘制动图(FuncAnimation()参数)
  3. Spring Security and Shiro
  4. highcharts使用教程
  5. [转]Entity Framework 异常: ‘OFFSET‘ 附近有语法错误。\r\n在 FETCH 语句中选项 NEXT 的用法无效
  6. git pull 默认拉取远端其他分支 问题解决
  7. html5 canvas 画图移动端出现锯齿毛边的解决方法
  8. Oracle Ora 错误解决方案合集
  9. 安装了低版本Jdk后eclipse无法打开的终极解决方法
  10. Freescale 车身控制模块(BCM) 解决方案