手机版

JavaScript范围链介绍

时间:2021-11-16 来源:互联网 编辑:宝哥软件园 浏览:

我写了一篇关于什么是JavaScript闭包来理解闭包的文章,觉得非常清楚,可以简单地理解闭包的成因。但是看了评论,我说只有理解范围链和活动对象,才能真正理解闭包。起初,我不同意。后来和公司同事交流,发现范围和执行环境真的很重要,很基础,对理解JavaScript闭包很有帮助。因此,我写的是对范围和执行环境的理解。范围范围是变量和函数的可访问范围,它控制变量和函数的可见性和生命周期。在JavaScript中,变量的作用域包括全局作用域和局部作用域。简单的JavaScript作用域很容易理解。在一些类似C的编程语言中,花括号中的每段代码都有自己的作用域,变量在声明它们的代码部分之外是不可见的,这被称为块级作用域。这就是JavaScript容易让初学者误解的地方。JavaScript没有块级作用域,只有函数级作用域:变量在函数体及其声明变量的子函数中可见。未在函数中声明或未声明var的变量是具有全局作用域的全局变量,窗口对象的所有属性都具有全局作用域;它可以在代码中的任何地方被访问。在函数内部声明并用var修改的变量是局部变量,只能在函数体中使用。虽然没有使用var,但是函数的参数仍然是局部变量。复制代码如下: var a=3;//全局变量函数fn(b){ //局部变量c=2;//全局变量var d=5;//局部变量函数SubN(){ var e=d;//对于(var I=0;i3;I){ console . write(I);} alert(I);//3,在for循环中声明,在循环外的函数中仍然可见,没有block scope } } alert(c);//在函数中声明,没有var修改,仍然是全局变量。只要明白JavaScript没有块作用域,简单的JavaScript作用域就很容易理解。另一个容易让初学者混淆的地方是,JavaScript变量可以提前解释和声明。名字有很多,但他们说一件事。虽然JavaScript被解释和执行,但是,它不是一步一步解释和执行的。在真正的解释和执行之前,JavaScript解释器会对代码进行预解析,并提前解释变量和函数声明部分,这意味着我们可以在函数声明语句之前调用函数,这是大多数人习惯的。然而,乍一看,解释变量会很奇怪。复制代码如下: console . log(a);//未定义的var a=3;console . log(a);//3 console . log(b);//执行上述代码前,未定义unsughtreference错误:b,var a=3;的声明部分已被预解析(但不会执行赋值语句),因此它将首次未定义,并且不会报告任何错误。赋值语句执行后,会得到3。从前面的代码中删除最后一句与下面的代码具有相同的效果。复制的代码如下: var a;console . log(a);//未定义a=3;console . log(a);//3但是,如果是这样的话,JavaScript的作用域很简单,但是由于函数子函数带来的问题,作用域就没有那么简单了。大人物登场——的执行环境或运行时上下文:执行上下文定义了变量或函数可以访问的其他数据,并决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象。执行环境中定义的所有变量和函数都将存储在这个对象中,解析器将在处理数据时访问这个内部对象。全局执行环境是最外层的执行环境。在web浏览器中,全局执行环境是窗口对象,因此所有全局变量和函数都是作为窗口对象的属性和放大倍数创建的。

每个函数都有自己的执行环境。当执行流进入一个函数时,该函数的环境将被推入一个函数栈。函数执行后,会弹出执行环境并销毁,其中存储的所有变量和函数定义都会被销毁。控制权将返回到以前的执行环境,只有当应用程序退出(浏览器关闭)时,全局执行环境才会被销毁。当在环境中执行范围链代码时,它将创建变量对象的范围链(sc),以确保对执行环境有权访问的变量和函数的有序访问。作用域中的第一个对象始终是当前执行代码所在环境中的变量对象(VO)。复制代码的代码如下:function a(x,y){ var b=x y;返回b;}创建函数A时,其作用域链填充到全局对象中,全局对象中有所有全局变量image

如果执行环境是函数,它的激活对象(AO)是作用域链中的第一个对象,第二个对象是包含环境,下一个是包含环境的包含环境。复制代码如下:函数a(x,y){ var b=x y;返回b;} var tatal=a(5,10);此时,var total=a(5,10);语句的范围链如下image

在函数运行的过程中,标识符的解析是一个沿着作用域链逐级搜索的过程,从第一个对象开始,一步一步往回走,直到找到同名的标识符。找到后,不会继续遍历,如果没有找到,会报错。让我们在关闭前看一下博客的总结:只要有调用内部函数的可能,JavaScript就需要保留被引用的函数。而且,JavaScript运行时需要跟踪引用这个内部函数的所有变量,直到最后一个变量被丢弃,JavaScript的垃圾收集器可以释放相应的内存空间。回想起来,我明白了很多。父函数定义的变量在子函数的作用域链中,子函数不会被破坏。其作用域链中的所有变量和函数都将得到维护,而不会被销毁。复制代码如下: for(var I=0;ielelements . length;i ){元素[i]。onclick=function(){ alert(I);}}这是上一篇博客中提到的经典错误。每当一个元素点击alert,它就是长度。在这段代码中,为元素绑定的click事件处理程序的范围链是这样的image

由于内部函数(click事件处理程序可以随时调用),其作用域链无法被破坏(且不说本例中我在全局作用域内,只能卸载或破坏页面),I的值在for循环执行后始终保持长度值,因此每次触发onclick时,报警长度才可用。复制代码如下: for(var I=0;ielelements . length;i ){(函数(n){ elements[n])。onclick=function(){ alert(n);} })(I);}为什么这样可以?此时,onclick引用的变量变成了n,由于函数的立即执行,每个onclick函数都会在作用域链中保留对应的n(0~length-1),这在此时是可以的。其实,在了解了执行环境和范围链之后,闭包就翻了个身,成为了一个显而易见的东西,但是不能滥用。从上面的例子可以看出,闭包会使子函数保留其作用域链中的所有变量以及函数和内存,这消耗了大量的内存。使用它时,尝试销毁父函数不再使用的变量。

版权声明:JavaScript范围链介绍是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。