Heim >Web-Frontend >js-Tutorial >Verständnis von JavaScript Scoping & Hoisting (2)_Javascript-Kenntnisse

Verständnis von JavaScript Scoping & Hoisting (2)_Javascript-Kenntnisse

WBOY
WBOYOriginal
2016-05-16 15:31:171231Durchsuche

Scoping & Heben

var a = 1;

function foo() {
  if (!a) {
    var a = 2;
  }
  alert(a);
};

foo();

Welche Ergebnisse liefert der obige Code, wenn er ausgeführt wird?

Obwohl dies für erfahrene Programmierer nur ein Kinderspiel ist, folge ich dennoch den gängigen Vorstellungen von Anfängern und beschreibe es:

1. Erstellte die globale Variable a und definierte ihren Wert als 1
2. Funktion foo
erstellt 3. Im Funktionskörper von foo wird die if-Anweisung nicht ausgeführt, da !a die Variable a in einen booleschen falschen Wert umwandelt, d. h. false
4. Überspringen Sie den bedingten Zweig, Alarmvariable a, das Endergebnis sollte 1 ausgegeben werden

Nun, es scheint eine einwandfreie Argumentation zu sein, aber das Überraschende ist: Die Antwort ist tatsächlich 2! Warum?

Keine Sorge, ich erkläre es dir. Lassen Sie mich zunächst sagen, dass dies kein Fehler ist, sondern eine (inoffizielle) Funktion des JavaScript-Sprachinterpreters. Jemand (Ben Cherry) hat diese Funktion als Hoisting (Es gibt noch keine Standardübersetzung, die gebräuchlichere ist Promote).

Erklärungen und Definitionen

Um das Heben zu verstehen, schauen wir uns zunächst eine einfache Situation an:

var a = 1;

Haben Sie jemals darüber nachgedacht, was genau passiert, wenn der obige Code ausgeführt wird?

Wissen Sie, was diesen Code betrifft, welche der beiden Anweisungen „Variable a deklarieren“ oder „Variable a definieren“ richtig ist?
•Das folgende Beispiel heißt „Variablen deklarieren“:

var a;

•Das folgende Beispiel heißt „Variablen definieren“:

var a = 1;

•Deklaration: Es bedeutet, dass Sie die Existenz von etwas, beispielsweise einer Variablen oder einer Funktion, behaupten, aber nicht erklären, was so etwas ist, sondern dem Interpreter nur sagen, dass so etwas existiert; •Definition: Dies bedeutet, dass Sie die spezifische Implementierung von etwas angeben, z. B. den Wert einer Variablen oder den Funktionskörper einer Funktion, und die Bedeutung einer solchen Sache genau ausdrücken.


Zusammenfassend:

var a; // Dies ist eine Aussage

a = 1; // Das ist die Definition (Aufgabe)

var a = 1; // Kombiniere zwei zu eins: Deklariere die Existenz einer Variablen und weise ihr einen Wert zu

Hier ist der entscheidende Punkt: Wenn Sie denken, Sie hätten nur eine Sache getan (var a = 1), hat der Interpreter diese Sache tatsächlich in zwei Schritte zerlegt, einer ist die Deklaration (var a) und der andere ist die Definition ( a = 1).

Was hat das mit Heben zu tun?

Um auf das verwirrende Beispiel am Anfang zurückzukommen, möchte ich Ihnen erklären, wie der Interpreter Ihren Code analysiert:

var a;
a = 1;

function foo() {
  var a;    // 关键在这里
  if (!a) {
    a = 2;
  }
  alert(a);   // 此时的 a 并非函数体外的那个全局变量
}
Wie im Code gezeigt, deklariert der Interpreter nach Eingabe des Funktionskörpers eine neue Variable a, und unabhängig von der Bedingung der if-Anweisung wird der neuen Variablen a der Wert 2 zugewiesen. Wenn Sie es nicht glauben, können Sie „alert(a)“ außerhalb des Funktionskörpers ausführen und dann foo() ausführen, um die Ergebnisse zu vergleichen.

Scoping

Jemand könnte fragen: „Warum nicht die Variable a innerhalb der if-Anweisung deklarieren?“

Da JavaScript keinen Blockbereich (Block Scoping) hat, sondern nur einen Funktionsbereich (Function Scoping). Wenn Sie also ein Paar geschweifte Klammern {} sehen, bedeutet dies, dass ein neuer Bereich generiert wurde, der sich von C unterscheidet !

Wenn der Parser die if-Anweisung liest, erkennt er, dass eine Variablendeklaration und -zuweisung vorhanden ist. Daher erhöht der Parser seine Deklaration an die Spitze des aktuellen Bereichs (dies ist das Standardverhalten und kann nicht geändert werden). Dieses Verhalten wird als Heben bezeichnet.

OK, jeder versteht, verstehst du...

Verstehen bedeutet nicht, dass Sie es verwenden werden. Wenn ich nur möchte, dass Alert(a) die 1 erzeugt, was soll ich tun?

Neuen Bereich erstellen

Wenn alert(a) ausgeführt wird, wird nach dem Speicherort der Variablen a gesucht. Die Suche erfolgt vom aktuellen Bereich nach oben (oder nach außen) zum Bereich der obersten Ebene. Wenn sie nicht gefunden wird, wird „undefiniert“ gemeldet .

Da wir im Geschwisterbereich von Alert(a) die lokale Variable a erneut deklariert haben, meldet sie 2, sodass wir die Deklaration der lokalen Variablen a nach unten (oder nach innen) verschieben können, sodass Alert (a) Es kann nicht gefunden werden.

Denken Sie daran: JavaScript hat nur Funktionsumfang!


你或许在无数的 JavaScript 书籍和文章里读到过:“请始终保持作用域内所有变量的声明放置在作用域的顶部”,现在你应该明白为什么有此一说了吧?因为这样可以避免 Hoisting 特性给你带来的困扰(我不是很情愿这么说,因为 Hoisting 本身并没有什么错),也可以很明确的告诉所有阅读代码的人(包括你自己)在当前作用域内有哪些变量可以访问。但是,变量声明的提升并非 Hoisting 的全部。在 JavaScript 中,有四种方式可以让命名进入到作用域中(按优先级):

1.语言定义的命名:比如 this 或者 arguments,它们在所有作用域内都有效且优先级最高,所以在任何地方你都不能把变量命名为 this 之类的,这样是没有意义的
2.形式参数:函数定义时声明的形式参数会作为变量被 hoisting 至该函数的作用域内。所以形式参数是本地的,不是外部的或者全局的。当然你可以在执行函数的时候把外部变量传进来,但是传进来之后就是本地的了
3.函数声明:函数体内部还可以声明函数,不过它们也都是本地的了
4.变量声明:这个优先级其实还是最低的,不过它们也都是最常用的

另外,还记得之前我们讨论过 声明 和 定义 的区别吧?当时我并没有说为什么要理解这个区别,不过现在是时候了,记住:

Hosting 只提升了命名,没有提升定义

这一点和我们接下来要讲到的东西息息相关,请看:

函数声明与函数表达式的差别

先看两个例子:

function test() {
  foo();

  function foo() {
    alert("我是会出现的啦……");
  }
}

test();
function test() {
  foo();

  var foo = function() {
    alert("我不会出现的哦……");
  }
}

test();

同学,在了解了 Scoping & Hoisting 之后,你知道怎么解释这一切了吧?

在第一个例子里,函数 foo 是一个声明,既然是声明就会被提升(我特意包裹了一个外层作用域,因为全局作用域需要你的想象,不是那么直观,但是道理是一样的),所以在执行 foo() 之前,作用域就知道函数 foo 的存在了。这叫做函数声明(Function Declaration),函数声明会连通命名和函数体一起被提升至作用域顶部。

然而在第二个例子里,被提升的仅仅是变量名 foo,至于它的定义依然停留在原处。因此在执行 foo() 之前,作用域只知道 foo 的命名,不知道它到底是什么,所以执行会报错(通常会是:undefined is not a function)。这叫做函数表达式(Function Expression),函数表达式只有命名会被提升,定义的函数体则不会。

尾记:Ben Cherry 的原文解释的更加详细,只不过是英文而已。我这篇是借花献佛,主要是更浅显的解释给初学者听,若要看更多的示例,请移步原作,谢谢。

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