Heim  >  Artikel  >  Web-Frontend  >  Javascript-detaillierte Erklärung von Scope, Scope-Kette und Abschluss (Bild und Text)

Javascript-detaillierte Erklärung von Scope, Scope-Kette und Abschluss (Bild und Text)

黄舟
黄舟Original
2017-03-21 14:23:371232Durchsuche

In diesem Artikel werden hauptsächlich Diagramme Javascript vorgestellt – Umfang, Bereichskette, Abschluss und andere Kenntnisse. Hat einen sehr guten Referenzwert. Werfen wir einen Blick darauf mit dem Editor unten

Was ist Umfang?

Der Bereich ist eine Regel, die während der Codekompilierungsphase festgelegt wird und den zugänglichen Bereich von Variablen und Funktionen festlegt. Globale Variablen haben einen globalen Gültigkeitsbereich und lokale Variablen haben einen lokalen Gültigkeitsbereich. js ist eine Sprache ohne Bereich auf Blockebene (Codeblöcke in geschweiften Klammern, einschließlich if, for und anderen Anweisungen oder einzelne Codeblöcke in geschweiften Klammern, können keinen lokalen Bereich bilden), sodass die lokale Rolle von js Die Domäne wird gebildet von und nur der Codeblock, der in den geschweiften Klammern der Funktion definiert ist, also der Funktionsumfang.

Was ist eine Scope-Kette?

Bereichskette ist die Implementierung von Bereichsregeln. Durch die Implementierung der Bereichskette kann auf Variablen innerhalb ihres Bereichs zugegriffen und Funktionen innerhalb ihres Bereichs aufgerufen werden.

Eine Bereichskette ist eine verknüpfte Liste, auf die nur in einer Richtung zugegriffen werden kann. Jeder Knoten in dieser verknüpften Liste ist ein Variables Objekt des Ausführungskontexts (es ist ein aktives Objekt, wenn die Der Code wird ausgeführt. Eine einseitig verknüpfte Liste. Der Kopf (der erste Knoten, auf den zugegriffen werden kann) ist immer das variable Objekt (aktives Objekt) der Funktion, die gerade aufgerufen und ausgeführt wird, und der Schwanz ist immer das globale aktive Objekt.

Die Bildung einer Scope-Kette?

Schauen wir uns den Bildungsprozess der Scope-Kette aus der Ausführung eines Codeabschnitts an.

function fun01 () {
 console.log('i am fun01...');
 fun02();
}
function fun02 () {
 console.log('i am fun02...');
}
fun01();

Javascript-detaillierte Erklärung von Scope, Scope-Kette und Abschluss (Bild und Text)

Datenzugriffsprozess

Wie oben gezeigt, folgt das Programm beim Zugriff auf eine Variable der Einwegmethode Bereichskette Um auf Merkmale zuzugreifen, suchen Sie zunächst im AO des Kopfknotens. Wenn nicht, suchen Sie im AO des nächsten Knotens bis zum Endknoten (globaler AO). Wenn es in diesem Prozess gefunden wird, wird es gefunden. Wenn es nicht gefunden wird, wird ein undefinierter Fehler gemeldet.

Erweitern Sie die Gültigkeitsbereichskette

Aus der Bildung der Gültigkeitsbereichskette oben können wir erkennen, dass jeder Knoten in der Kette beim Aufruf der Funktion zum Kettenkopf verschoben wird AO der aktuellen Funktion, und eine andere Möglichkeit, Knoten zu bilden, besteht darin, „die Bereichskette zu erweitern“, das heißt, einen gewünschten Objektbereich an der Spitze der Bereichskette einzufügen. Es gibt zwei Möglichkeiten, die Geltungsbereichskette zu erweitern:

1.mit Anweisung

function fun01 () {
 with (document) {
  console.log('I am fun01 and I am in document scope...')
 }
}
fun01();

Javascript-detaillierte Erklärung von Scope, Scope-Kette und Abschluss (Bild und Text)

2. Der Catch-Block der Try-Catch-Anweisung

function fun01 () {
 try {
  console.log('Some exceptions will happen...')
 } catch (e) {
  console.log(e)
 }
}
fun01();

Javascript-detaillierte Erklärung von Scope, Scope-Kette und Abschluss (Bild und Text)

ps: Ich persönlich habe das Gefühl, dass es keinen gibt Sie müssen unbedingt die with-Anweisung verwenden, try Die Verwendung von -catch hängt auch von den Anforderungen ab. Ich persönlich verwende diese beiden nicht oft, aber während ich diesen Teil organisierte, habe ich mir einige unausgereifte Tipps zur Leistungsoptimierung auf der Ebene der Bereichskette ausgedacht.

Ein etwas unausgereifter Vorschlag zur Leistungsoptimierung durch die Bereichskette

1. Reduzieren Sie den Bereichskettenzugriff auf Variablenknoten

Hier passen wir eine Rangfolge namens „Suchentfernung“ an, die die Anzahl der Knoten in der Bereichskette darstellt, die das Programm durchläuft, um auf eine nicht undefinierte Variable zuzugreifen. Denn wenn die Variable am aktuellen Knoten nicht gefunden wird, springt sie zur Suche zum nächsten Knoten und muss außerdem feststellen, ob die gesuchte Variable im nächsten Knoten vorhanden ist. Je länger die „Suchentfernung“ ist, desto mehr „Sprung“- und „Beurteilungs“-Aktionen sind erforderlich und desto größer ist der Ressourcenaufwand, der sich auf die Leistung auswirkt. Diese Leistungslücke verursacht bei einigen Variablensuchvorgängen möglicherweise keine allzu großen Leistungsprobleme. Wenn Variablensuchvorgänge jedoch mehrmals ausgeführt werden, ist der Leistungsvergleich offensichtlicher.


(function(){
 console.time()
 var find = 1      //这个find变量需要在4个作用域链节点进行查找
 function fun () {
  function funn () {
   var funnv = 1;
   var funnvv = 2;
   function funnn () {
    var i = 0
    while(i <= 100000000){
     if(find){
      i++
     }
    }
   }
   funnn()
  }
  funn()
 }
 fun()
 console.timeEnd()
})()

Javascript-detaillierte Erklärung von Scope, Scope-Kette und Abschluss (Bild und Text)


(function(){
 console.time()
 function fun () {
  function funn () {
   var funnv = 1;
   var funnvv = 2;
   function funnn () {
    var i = 0
    var find = 1      //这个find变量只在当前节点进行查找
    while(i <= 100000000){
     if(find){
      i++
     }
    }
   }
   funnn()
  }
  funn()
 }
 fun()
 console.timeEnd()
})()

auf dem Mac Unter dem Browser Chrome Pro wurde ein Experiment durchgeführt und 100 Millionen Suchvorgänge durchgeführt.

Experimentelle Ergebnisse: Ersteres benötigt bei fünfmaliger Ausführung durchschnittlich 85,599 ms, letzteres bei fünfmaliger Ausführung durchschnittlich 63,127 ms.

2. Vermeiden Sie zu viele Variablendefinitionen auf dem Knoten AO in der Bereichskette

Der Hauptgrund, warum zu viele Variablendefinitionen Leistungsprobleme verursachen Es ist die „Beurteilungs“-Operation beim Finden von Variablen, die teuer ist. Wir verwenden mit zum Leistungsvergleich.

(function(){
 console.time()
 function fun () {
  function funn () {
   var funnv = 1;
   var funnvv = 2;
   function funnn () {
    var i = 0
    var find = 10
    with (document) {
     while(i <= 1000000){
      if(find){
       i++
      }
     }
    }
   }
   funnn()
  }
  funn()
 }
 fun()
 console.timeEnd()
})()

Javascript-detaillierte Erklärung von Scope, Scope-Kette und Abschluss (Bild und Text)

在mac pro的chrome浏览器下做实验,进行100万次查找运算,借助with使用document进行的延长作用域链,因为document下的变量属性比较多,可以测试在多变量作用域链节点下进行查找的性能差异。

实验结果:5次平均耗时558.802ms,而如果删掉with和document,5次平均耗时0.956ms。

当然,这两个实验是在我们假设的极端环境下进行的,结果仅供参考!

关于闭包

1.什么是闭包?

函数对象可以通过作用域链相互关联起来,函数体内的数据(变量和函数声明)都可以保存在函数作用域内,这种特性在计算机科学文献中被称为“闭包”。既函数体内的数据被隐藏于作用于链内,看起来像是函数将数据“包裹”了起来。从技术角度来说,js的函数都是闭包:函数都是对象,都关联到作用域链,函数内数据都被保存在函数作用域内。

2.闭包的几种实现方式

实现方式就是函数A在函数B的内部进行定义了,并且当函数A在执行时,访问了函数B内部的变量对象,那么B就是一个闭包。如下:

Javascript-detaillierte Erklärung von Scope, Scope-Kette und Abschluss (Bild und Text)

Javascript-detaillierte Erklärung von Scope, Scope-Kette und Abschluss (Bild und Text)

如上两图所示,是在chrome浏览器下查看闭包的方法。两种方式的共同点是都有一个外部函数outerFun(),都在外部函数内定义了内部函数innerFun(),内部函数都访问了外部函数的数据。不同的是,第一种方式的innerFun()是在outerFun()内被调用的,既声明和被调用均在同一个执行上下文内。而第二种方式的innerFun()则是在outerFun()外被调用的,既声明和被调用不在同一个执行上下文。第二种方式恰好是js使用闭包常用的特性所在:通过闭包的这种特性,可以在其他执行上下文内访问函数内部数据。

我们更常用的一种方式则是这样的:

//闭包实例
function outerFun () {
 var outerV1 = 10
 function outerF1 () {
  console.log(&#39;I am outerF1...&#39;)
 }
 function innerFun () {
  var innerV1 = outerV1
  outerF1()
 }
 return innerFun //return回innerFun()内部函数
}
var fn = outerFun()  //接到return回的innerFun()函数
fn()     //执行接到的内部函数innerFun()

此时它的作用域链是这样的:

Javascript-detaillierte Erklärung von Scope, Scope-Kette und Abschluss (Bild und Text)

3.闭包的好处及使用场景

js的垃圾回收机制可以粗略的概括为:如果当前执行上下文执行完毕,且上下文内的数据没有其他引用,则执行上下文pop出call stack,其内数据等待被垃圾回收。而当我们在其他执行上下文通过闭包对执行完的上下文内数据仍然进行引用时,那么被引用的数据则不会被垃圾回收。就像上面代码中的outerV1,放我们在全局上下文通过调用innerFun()仍然访问引用outerV1时,那么outerFun执行完毕后,outerV1也不会被垃圾回收,而是保存在内存中。另外,outerV1看起来像不像一个outerFun的私有内部变量呢?除了innerFun()外,我们无法随意访问outerV1。所以,综上所述,这样闭包的使用情景可以总结为:

(1)进行变量持久化。

(2)使函数对象内有更好的封装性,内部数据私有化。

进行变量持久化方面举个栗子:

我们假设一个需求时写一个函数进行类似id自增或者计算函数被调用的功能,普通青年这样写:

 var count = 0
 function countFun () {
  return count++
 }

这样写固然实现了功能,但是count被暴露在外,可能被其他代码篡改。这个时候闭包青年就会这样写:

function countFun () {
 var count = 0
 return function(){
  return count++
 }
}
var a = countFun()
a()

这样count就不会被不小心篡改了,函数调用一次就count加一次1。而如果结合“函数每次被调用都会创建一个新的执行上下文”,这种count的安全性还有如下体现:

function countFun () {
 var count = 0
 return {
  count: function () {
   count++
  },
  reset: function () {
   count = 0
  },
  printCount: function () {
   console.log(count)
  }
 }
}
var a = countFun()
var b = countFun()
a.count()
a.count()
b.count()
b.reset()
a.printCount()  //打印:2 因为a.count()被调用了两次
b.printCount()  //打印出:0 因为调用了b.reset()

以上便是闭包提供的变量持久化和封装性的体现。

4.闭包的注意事项

Da Variablen in Abschlüssen nicht wie andere normale Variablen durch Garbage Collection erfasst werden, sondern immer im Speicher vorhanden sind, kann die häufige Verwendung von Abschlüssen zu Leistungsproblemen führen.

Das obige ist der detaillierte Inhalt vonJavascript-detaillierte Erklärung von Scope, Scope-Kette und Abschluss (Bild und Text). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn