Heim >Web-Frontend >js-Tutorial >Folgen Sie mir, um die Fähigkeiten von Javascript Scope und Scope Chain_Javascript zu erlernen
Der Bereich ist eines der wichtigsten Konzepte in JavaScript. Wenn Sie JavaScript gut erlernen möchten, müssen Sie die Funktionsprinzipien des JavaScript-Bereichs und der Bereichskette verstehen. Der heutige Artikel bietet eine kurze Einführung in den JavaScript-Bereich und die Bereichskette und hofft, jedem dabei zu helfen, JavaScript besser zu lernen.
1. JavaScript-Bereich
Jede Programmiersprache hat das Konzept des Bereichs. Einfach ausgedrückt ist der Bereich der zugängliche Bereich von Variablen und Funktionen, das heißt, der Bereich steuert die Sichtbarkeit und den Lebenszyklus von Variablen und Funktionen. In JavaScript gibt es zwei Arten von Variablenbereichen: den globalen Bereich und den lokalen Bereich.
1. Globaler Geltungsbereich
Objekte, auf die überall im Code zugegriffen werden kann, haben einen globalen Gültigkeitsbereich. Im Allgemeinen haben die folgenden Situationen einen globalen Gültigkeitsbereich:
(1) Die äußerste Funktion und die außerhalb der äußersten Funktion definierten Variablen haben einen globalen Gültigkeitsbereich, zum Beispiel:
var authorName="小平果"; function doSomething(){ var blogName="你不知道的JavaScript"; function innerSay(){ alert(blogName); } innerSay(); } alert(authorName); //小平果 alert(blogName); //脚本错误 doSomething(); //你不知道的JavaScript innerSay() //脚本错误
(2) Alle undefinierten und direkt zugewiesenen Variablen werden automatisch mit globalem Gültigkeitsbereich deklariert, zum Beispiel:
function doSomething(){ var authorName="小平果"; blogName="你不知道的JavaScript"; alert(authorName); } doSomething(); //小平果 alert(blogName); //你不知道的JavaScript alert(authorName); //脚本错误
Die Variable blogName hat einen globalen Gültigkeitsbereich, während auf authorName außerhalb der Funktion nicht zugegriffen werden kann.
(3) Alle Fensterobjekteigenschaften haben einen globalen Geltungsbereich
Im Allgemeinen haben die integrierten Eigenschaften des Fensterobjekts einen globalen Geltungsbereich, wie z. B. window.name, window.location, window.top usw.
2. Lokaler Geltungsbereich
Im Gegensatz zum globalen Bereich ist der lokale Bereich im Allgemeinen nur innerhalb eines festen Codefragments zugänglich, am häufigsten innerhalb einer Funktion. Daher werden Sie an manchen Stellen auch sehen, dass dieser Bereich als Funktionsbereich bezeichnet wird, wie zum Beispiel im folgenden BlogName von Both und die Funktion innerSay im Code haben nur einen lokalen Gültigkeitsbereich.
function doSomething(){ var blogName="你不知道的JavaScript"; function innerSay(){ alert(blogName); } innerSay(); } alert(blogName); //脚本错误 innerSay(); //脚本错误
2. Scope-Kette
In JavaScript sind Funktionen auch Objekte. Tatsächlich ist alles in JavaScript ein Objekt. Funktionsobjekte verfügen wie andere Objekte über Eigenschaften, auf die über Code zugegriffen werden kann, sowie über eine Reihe interner Eigenschaften, auf die nur die JavaScript-Engine zugreifen kann. Eine der internen Eigenschaften ist [[Scope]], definiert durch die dritte Ausgabe des ECMA-262-Standards. Diese interne Eigenschaft enthält die Sammlung von Objekten in dem Bereich, in dem die Funktion erstellt wird , die bestimmt, auf welche Daten die Funktion zugreifen kann.
Wenn eine Funktion erstellt wird, wird ihre Bereichskette mit Datenobjekten gefüllt, auf die in dem Bereich zugegriffen werden kann, in dem die Funktion erstellt wurde. Definieren Sie beispielsweise die folgende Funktion:
function add(num1,num2) { var sum = num1 + num2; return sum; }
Wenn die Funktion add erstellt wird, wird ihre Bereichskette mit einem globalen Objekt gefüllt, das alle globalen Variablen enthält, wie in der Abbildung unten gezeigt (Hinweis: Das Bild zeigt nur einen Teil aller Variablen):
Der Umfang der Funktion add wird während der Ausführung verwendet. Führen Sie beispielsweise den folgenden Code aus:
var total = add(5,10);
执行此函数时会创建一个称为“运行期上下文(execution context)”的内部对象,运行期上下文定义了函数执行时的环境。每个运行期上下文都有自己的作用域链,用于标识符解析,当运行期上下文被创建时,而它的作用域链初始化为当前运行函数的[[Scope]]所包含的对象。
这些值按照它们出现在函数中的顺序被复制到运行期上下文的作用域链中。它们共同组成了一个新的对象,叫“活动对象(activation object)”,该对象包含了函数的所有局部变量、命名参数、参数集合以及this,然后此对象会被推入作用域链的前端,当运行期上下文被销毁,活动对象也随之销毁。新的作用域链如下图所示:
在函数执行过程中,没遇到一个变量,都会经历一次标识符解析过程以决定从哪里获取和存储数据。该过程从作用域链头部,也就是从活动对象开始搜索,查找同名的标识符,如果找到了就使用这个标识符对应的变量,如果没找到继续搜索作用域链中的下一个对象,如果搜索完所有对象都未找到,则认为该标识符未定义。函数执行过程中,每个标识符都要经历这样的搜索过程。
三、作用域链和代码优化
从作用域链的结构可以看出,在运行期上下文的作用域链中,标识符所在的位置越深,读写速度就会越慢。如上图所示,因为全局变量总是存在于运行期上下文作用域链的最末端,因此在标识符解析的时候,查找全局变量是最慢的。所以,在编写代码的时候应尽量少使用全局变量,尽可能使用局部变量。一个好的经验法则是:如果一个跨作用域的对象被引用了一次以上,则先把它存储到局部变量里再使用。例如下面的代码:
function changeColor(){ document.getElementById("btnChange").onclick=function(){ document.getElementById("targetCanvas").style.backgroundColor="red"; }; }
这个函数引用了两次全局变量document,查找该变量必须遍历整个作用域链,直到最后在全局对象中才能找到。这段代码可以重写如下:
function changeColor(){ var doc=document; doc.getElementById("btnChange").onclick=function(){ doc.getElementById("targetCanvas").style.backgroundColor="red"; }; }
这段代码比较简单,重写后不会显示出巨大的性能提升,但是如果程序中有大量的全局变量被从反复访问,那么重写后的代码性能会有显著改善。
四、改变作用域链
函数每次执行时对应的运行期上下文都是独一无二的,所以多次调用同一个函数就会导致创建多个运行期上下文,当函数执行完毕,执行上下文会被销毁。每一个运行期上下文都和一个作用域链关联。一般情况下,在运行期上下文运行的过程中,其作用域链只会被 with 语句和 catch 语句影响。
with语句是对象的快捷应用方式,用来避免书写重复代码。例如:
function initUI(){ with(document){ var bd=body, links=getElementsByTagName("a"), i=0, len=links.length; while(i < len){ update(links[i++]); } getElementById("btnInit").onclick=function(){ doSomething(); }; } }
这里使用width语句来避免多次书写document,看上去更高效,实际上产生了性能问题。
当代码运行到with语句时,运行期上下文的作用域链临时被改变了。一个新的可变对象被创建,它包含了参数指定的对象的所有属性。这个对象将被推入作用域链的头部,这意味着函数的所有局部变量现在处于第二个作用域链对象中,因此访问代价更高了。如下图所示:
因此在程序中应避免使用with语句,在这个例子中,只要简单的把document存储在一个局部变量中就可以提升性能。
另外一个会改变作用域链的是try-catch语句中的catch语句。当try代码块中发生错误时,执行过程会跳转到catch语句,然后把异常对象推入一个可变对象并置于作用域的头部。在catch代码块内部,函数的所有局部变量将会被放在第二个作用域链对象中。示例代码:
try{ doSomething(); }catch(ex){ alert(ex.message); //作用域链在此处改变 }
请注意,一旦catch语句执行完毕,作用域链机会返回到之前的状态。try-catch语句在代码调试和异常处理中非常有用,因此不建议完全避免。你可以通过优化代码来减少catch语句对性能的影响。一个很好的模式是将错误委托给一个函数处理,例如:
try{ doSomething(); }catch(ex){ handleError(ex); //委托给处理器方法 }
Im optimierten Code ist die handleError-Methode der einzige Code, der in der Catch-Klausel ausgeführt wird. Diese Funktion erhält als Parameter ein Ausnahmeobjekt, sodass Sie Fehler flexibler und einheitlicher behandeln können. Da nur eine Anweisung ausgeführt wird und auf keine lokalen Variablen zugegriffen wird, haben vorübergehende Änderungen in der Bereichskette keinen Einfluss auf die Codeleistung.
Das Obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, dass jeder durch diesen Artikel den Umfang und die Umfangskette von JavaScript besser versteht und wir gemeinsam Fortschritte machen können.