手机版

对JavaScript范围和范围链的深入分析

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

范围是JavaScript最重要的概念之一。要学好JavaScript,需要了解JavaScript作用域和作用域链的工作原理。今天,本文对JavaScript范围和范围链做一个简单的介绍,希望能帮助大家更好的学习JavaScript。JavaScript范围任何编程语言都有范围的概念。简单来说,范围就是变量和函数的可访问范围,也就是说,范围控制着变量和函数的可见性和生命周期。在JavaScript中,变量的作用域包括全局作用域和局部作用域。1.全局范围:可以在代码中任何地方访问的对象都有全局范围。一般来说,有全局作用域的情况有几种:(1)最外层函数和最外层函数外定义的变量具有全局作用域。例如,复制的代码如下:varauthorName='山涧';functiondosomeing(){ varblogName=' ';functioninnerSay(){ alert(blogName);} innerSay();} alert(AuthorName);//山溪警报(blogName);//脚本错误dosome();//innerSay()//脚本错误(2)所有没有定义直接赋值的变量都自动声明为具有全局作用域,例如复制的代码如下: function do something(){ var author name=' mountain stream ';blogName=警报(AuthorName);} alert(Blogname);//alert(AuthorName);//脚本错误变量blogName具有全局作用域,而authorName不能在函数外访问。(3)窗口对象的所有属性都具有全局范围。通常,窗口对象的所有内置属性都有全局范围,如window.name、window.location、window.top等。2.本地范围与全局范围相反。局部范围通常只能在固定的代码片段中访问,最常见的是在函数内部。在某些地方,有些人会把这个范围看作一个功能范围。例如,下面代码中的blogName和function innerSay只有局部作用域。复制代码如下:函数do something(){ varblogname=' ';functioninnerSay(){ alert(blogName);} innerSay();} alert(Blogname);//脚本错误innerSay();//脚本错误范围链在JavaScript中,函数也是对象。事实上,JavaScript中的一切都是对象。像其他对象一样,函数具有可以通过代码访问的属性和一系列只能由JavaScript引擎访问的内部属性。内部属性之一是[[范围]],由第三版ECMA-262标准定义。该内部属性包含创建函数的范围内的一组对象。这个集合被称为函数的作用域链,它决定了函数可以访问哪些数据。当一个函数被创建时,它的作用域链将在创建该函数的作用域中被可访问的数据对象填充。例如,定义以下函数:复制代码如下:functionadd (num1,num 2){ varsum=num 1 num 2;returnsum}创建函数add时,其作用域链将填充一个全局对象,该对象包含所有全局变量,如下图所示(注:图片仅举例说明了所有变量中的一部分):

add函数的作用域将在执行过程中使用。例如,执行以下代码:复制代码如下:VARTTAL=ADD (5,10);执行该函数时,会创建一个名为“执行上下文”的内部对象,该对象定义了函数执行时的环境。每个运行时上下文都有自己的标识符解析范围链。创建运行时上下文时,其范围链被初始化为当前运行函数的[[范围]]中包含的对象。这些值按照它们在函数中出现的顺序复制到运行时上下文的范围链中。它们一起形成了一个新的对象,称为“激活对象”,它包含所有局部变量、命名参数、参数集和函数的这个。然后,这个对象将被推到范围链的前面,当运行时上下文被销毁时,活动对象也将被销毁。新的范围链如下所示:

在函数执行过程中,如果没有遇到变量,会经过一个标识符解析过程,决定在哪里获取和存储数据。这个过程从作用域链的头开始,即从活动对象开始,并搜索同名的标识符。如果找到,则使用对应于该标识符的变量。如果没有找到作用域链中的下一个对象,如果搜索后没有找到所有对象,则认为标识符未定义。在函数执行期间,每个标识符都必须经过这样的搜索过程。范围链与代码优化从范围链的结构可以看出,标识符在运行时上下文的范围链中越深,读写速度就会越慢。如上图所示,因为全局变量总是存在于运行时上下文范围链的末端,所以在解析标识符时找到全局变量是最慢的。因此,在编写代码时,我们应该使用尽可能少的全局变量和尽可能多的局部变量。一个很好的经验法则是,如果一个跨范围对象被引用了不止一次,那么它应该在被使用之前被存储在局部变量中。下面的代码,例如:按如下方式复制代码: function change color(){ document . getelementbyid(' BTN change ')。onclick=function(){ document . getelementbyid(' target canvas ')。style.backgroundcolor=' red};}此函数引用全局变量文档两次。要找到这个变量,您必须遍历整个范围链,直到它最终在全局对象中找到。这段代码可以改写如下:复制代码如下: FunctionChangeColor(){ vardoc=document;doc.getElementById('btnChange ')。onclick=function(){ doc . getelementbyid(' targetCanvas '). style . background color=' red ';};}这段代码比较简单,重写后不会表现出很大的性能提升,但是如果从中反复访问程序中的大量全局变量,重写后的代码性能会有明显的提升。每次执行作用域链函数时改变其对应的运行时上下文是唯一的,因此多次调用同一个函数会导致创建多个运行时上下文,当函数执行时,执行上下文会被破坏。每个运行时上下文都与一个范围链相关联。一般来说,运行时上下文的范围链只会受到with语句和catch语句的影响。with语句是一个快速的对象应用程序,可以避免编写重复的代码。示例:复制代码如下: functioninitui(){ with(document){ varbd=body,links=getelementsbytagname ('a '),I=0,len=links.lengthwhile(ilen){ update(link[I]);} getElementById('btnInit ')。onclick=function(){ dosometing();};}}在这里,width语句是用来避免多次写文档的,这样看起来效率更高,实际上会造成性能问题。当代码运行到with语句时,运行时上下文的范围链会临时更改。将创建一个新的变量对象,它包含参数指定的对象的所有属性。这个对象将被推入作用域链的头部,这意味着函数的所有局部变量现在都在第二个作用域链对象中,因此访问成本更高。如下图所示:

因此,在程序中应该避免with语句。在本例中,简单地将文档存储在局部变量中可以提高性能。另一个会改变范围链的是try-catch语句中的catch语句。当try代码块中出现错误时,执行将跳转到catch语句,然后将异常对象推入变量对象,并将其放在作用域的头部。在catch代码块中,函数的所有局部变量都将放在第二个作用域链对象中。示例代码:复制代码如下: try { dosome();} catch(ex){ alert(ex . message);//此处作用域链发生变化}请注意,一旦执行catch语句,作用域链将返回到之前的状态。Try-catch语句在代码调试和异常处理中非常有用,因此不建议完全避免它们。通过优化代码,可以减少catch语句对性能的影响。一个好的模式是将错误委托给一个函数进行处理,例如复制代码如下: try { dosometing();} catch(ex){ handleError(ex);//委托给处理器方法}优化代码,handleError方法是catch子句中唯一执行的代码。这个函数以异常对象为参数,这样可以更加灵活统一地处理错误。由于只执行一条语句,不访问局部变量,作用域链的临时变化不会影响代码性能。

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