宝哥软件园

向我学习javascript的范围和范围链

编辑:宝哥软件园 来源:互联网 时间:2021-09-22

范围是JavaScript最重要的概念之一。要学好JavaScript,需要了解JavaScript作用域和作用域链的工作原理。今天,本文对JavaScript范围和范围链做一个简单的介绍,希望能帮助大家更好的学习JavaScript。一、JavaScript作用域。

任何编程语言都有范围的概念。简单来说,范围就是变量和函数的可访问范围,也就是说,范围控制着变量和函数的可见性和生命周期。在JavaScript中,变量的作用域包括全局作用域和局部作用域。

1.全局范围(Global Scope)。

可以在代码中任何地方访问的对象都具有全局范围。一般来说,以下情况具有全局范围:

(1)最外面的函数和在最外面的函数之外定义的变量具有全局范围,例如:

Var authorName='肖平国';Function do某物(){var blog name=' JavaScript你不知道';函数innerSay(){ alert(blogName);} innerSay();} alert(AuthorName);//萧平国警戒(blogName);//脚本错误dosome();//JavaScript onnersy()//您不知道的脚本错误(2)所有未定义直接赋值的变量都被自动声明为具有全局作用域,例如:

函数做某事(){var作者名='肖平国';BlogName='你不知道的JavaScript ';警报(AuthorName);} DosMexing();//萧平国警戒(blogName);//JavaScriptalert(authorName)你不知道;//脚本错误变量blogName具有全局作用域,而authorName不能在函数外访问。

(3)窗口对象的所有属性都具有全局范围。

通常,窗口对象的内置属性具有全局范围,如window.name、window.location、window.top等。

2.本地范围与全局范围相反。局部范围通常只能在固定的代码片段中访问,最常见的是在函数内部。在某些地方,有些人也会把这个范围看作功能范围。例如,下面代码中的blogName和function innerSay只有局部作用域。

Function do某物(){var blog name=' JavaScript你不知道';函数innerSay(){ alert(blogName);} innerSay();} alert(Blogname);//脚本错误innerSay();//脚本错误2。范围链。

在JavaScript中,函数也是对象。事实上,JavaScript中的一切都是对象。像其他对象一样,函数具有可以通过代码访问的属性和一系列只能由JavaScript引擎访问的内部属性。内部属性之一是[[范围]],由第三版ECMA-262标准定义。该内部属性包含创建函数的范围内的一组对象。这个集合被称为函数的作用域链,它决定了函数可以访问哪些数据。

当一个函数被创建时,它的作用域链将在创建该函数的作用域中被可访问的数据对象填充。例如,定义以下函数:

函数add(num1,num 2){ var sum=num 1 num 2;返回总和;}创建函数add时,其作用域链将填充一个全局对象,该对象包含所有全局变量,如下图所示(注:图片仅举例说明了部分变量):

add函数的作用域将在执行过程中使用。例如,执行以下代码:

var total=add(5,10);执行该函数时,会创建一个名为“执行上下文”的内部对象,该对象定义了函数执行时的环境。每个运行时上下文都有自己的标识符解析范围链。创建运行时上下文时,其范围链被初始化为当前运行函数的[[范围]]中包含的对象。

这些值按照它们在函数中出现的顺序复制到运行时上下文的范围链中。它们一起组成了一个新的对象,叫做“激活对象”,包含了函数的所有局部变量、命名参数、参数集和这个,然后这个对象会被推到作用域链的前面。当运行时上下文被销毁时,活动对象也将被销毁。下图显示了新的范围链:

在函数执行过程中,如果没有遇到变量,会经过一个标识符解析过程,决定在哪里获取和存储数据。这个过程从作用域链的头开始,即从活动对象开始,并搜索同名的标识符。如果找到,则使用对应于该标识符的变量。如果没有找到作用域链中的下一个对象,如果搜索后没有找到所有对象,则认为标识符未定义。在函数执行期间,每个标识符都必须经过这样的搜索过程。

第三,范围链和代码优化。

从作用域链的结构可以看出,标识符在运行时上下文的作用域链中越深,读写速度就会越慢。如上图所示,因为全局变量总是存在于运行时上下文范围链的末端,所以在解析标识符时找到全局变量是最慢的。因此,在编写代码时,我们应该使用尽可能少的全局变量和尽可能多的局部变量。一个很好的经验法则是,如果一个跨范围对象被引用了不止一次,那么它应该在被使用之前被存储在局部变量中。例如,以下代码:

function change color(){ document . getelementbyid(' btnChange ')。onclick=function(){ document . getelementbyid(' targetCanvas '). style . background color=' red ';};}此函数引用全局变量文档两次。要找到这个变量,您必须遍历整个范围链,直到它最终在全局对象中找到。这段代码可以改写如下:

函数ChangeColor(){ var doc=document;doc.getElementById('btnChange ')。onclick=function(){ doc . getelementbyid(' targetCanvas '). style . background color=' red ';};}这段代码比较简单,重写后不会表现出很大的性能提升,但是如果从中反复访问程序中的大量全局变量,重写后的代码性能会有明显的提升。

第四,改变范围链。

每次执行一个函数时,对应的运行时上下文是唯一的,因此多次调用同一个函数将导致创建多个运行时上下文。当函数被执行时,执行上下文将被销毁。每个运行时上下文都与一个范围链相关联。一般来说,运行时上下文的范围链只会受到with语句和catch语句的影响。

with语句是一个快速的对象应用程序,可以避免编写重复的代码。例如:

函数initUI(){ with(document){ var BD=body,links=getElementsByTagName('a '),i=0,len=links.lengthwhile(I len){ update(link[I]);} getElementById('btnInit ')。onclick=function(){ dosometing();};}}在这里,width语句是用来避免多次写文档的,这样看起来效率更高,实际上会造成性能问题。

当代码运行到with语句时,运行时上下文的范围链会临时更改。将创建一个新的变量对象,其中包含参数指定的对象的所有属性。这个对象将被推入作用域链的头部,这意味着函数的所有局部变量现在都在第二个作用域链对象中,因此访问成本更高。如下图所示:

因此,在程序中应该避免with语句。在本例中,简单地将文档存储在局部变量中可以提高性能。

另一个会改变范围链的是try-catch语句中的catch语句。当try代码块中出现错误时,执行将跳转到catch语句,然后将异常对象推入变量对象,并将其放在作用域的头部。在catch代码块中,函数的所有局部变量都将放在第二个作用域链对象中。示例代码:

请尝试{ DosMexing();} catch(ex){ alert(ex . message);//此处作用域链发生变化}请注意,一旦执行catch语句,作用域链将返回到之前的状态。Try-catch语句在代码调试和异常处理中非常有用,因此不建议完全避免它们。通过优化代码,可以减少catch语句对性能的影响。一个好的模式是将错误委托给一个函数,例如:

请尝试{ DosMexing();} catch(ex){ handleError(ex);//委托给处理器方法}优化代码,handleError方法是catch子句中唯一执行的代码。这个函数以异常对象为参数,这样可以更加灵活统一地处理错误。由于只执行一条语句,不访问局部变量,作用域链的临时变化不会影响代码性能。

以上就是本文的全部内容。希望大家多了解javascript的范围和范围链,一起进步。

更多资讯
游戏推荐
更多+