In JavaScript erstellt ein Code-, Funktions- oder Modulblock einen Bereich für eine Variable. Beispielsweise erstellt der Codeblock if
einen Bereich für die Variable message
: if
代码块为变量 message
创建作用域:
if (true) { const message = 'Hello'; console.log(message); // 'Hello' } console.log(message); // throws ReferenceError
在 if
代码块作用域内可以访问 message
。但是在作用域之外,该变量不可访问。
好的,这是作用域的简短介绍。如果你想了解更多信息,建议阅读我的文章用简单的词解释 JavaScript 作用域。
以下是 5 种有趣的情况,其中 JavaScript 作用域的行为与你预期的不同。你可能会研究这些案例以提高对作用域的了解,或者只是为面试做准备。
思考以下代码片段:
const colors = ['red', 'blue', 'white']; for (let i = 0, var l = colors.length; i < l; i++) { console.log(colors[i]); // 'red', 'blue', 'white' } console.log(l); // ??? console.log(i); // ???
当你打印 l
和 i
变量时会发生什么?
console.log(l)
输出数字 3
,而 console.log(i)
则抛出 ReferenceError
。
l
变量是使用 var
语句声明的。你可能已经知道,var
变量仅受函数体作用域限制而并非代码块。
相反,变量 i
使用 let
语句声明。因为 let
变量是块作用域的,所以 i
仅在 for
循环作用域内才可访问。
把 l
声明从 var l = colors.length
改为 const l = colors.length
。现在变量 l
被封装在 for
循环体内。
在以下代码段中:
// ES2015 env { function hello() { return 'Hello!'; } } hello(); // ???
调用 hello()
会怎样? (代码段在 ES2015 环境中执行)
因为代码块为函数声明创建了作用域,所以在 ES2015 环境中调用 hello()
会引发 ReferenceError: hello is not defined
。
有趣的是,在 ES2015 之前的环境中,在执行上述代码段时不会抛出错误。 你知道为什么吗?请在下面的评论中写下你的答案!
你可以在代码块中导入模块吗?
if (true) { import { myFunc } from 'myModule'; // ??? myFunc(); }
上面的脚本将触发错误: 'import' and 'export' may only appear at the top-level
。
你只能在模块文件的最顶级作用域(也称为模块作用域)中导入模块。
始终从模块作用域导入模块。另外一个好的做法是将 import
语句放在源文件的开头:
import { myFunc } from 'myModule'; if (true) { myFunc(); }
ES2015 的模块系统是静态的。通过分析 JavaScript 源代码而不是执行代码来确定模块的依赖关系。所以在代码块或函数中不能包含 import
语句,因为它们是在运行时执行的。
思考以下函数:
let p = 1; function myFunc(p = p + 1) { return p; } myFunc(); // ???
调用 myFunc()
会发生什么?
当调用函数 myFunc()
时,将会引发错误: ReferenceError: Cannot access 'p' before initialization
。
发生这种情况是因为函数的参数具有自己的作用域(与函数作用域分开)。参数 p = p + 1
等效于 let p = p + 1
。
让我们仔细看看 p = p + 1
。
首先,定义变量 p
。然后 JavaScript 尝试评估默认值表达式 p + 1
,但此时绑定 p
已经创建但尚未初始化(不能访问外部作用域的变量 let p = 1
)。因此抛出一个错误,即在初始化之前访问了 p
。
为了解决这个问题,你可以重命名变量 let p = 1
,也可以重命名功能参数 p = p + 1
。
让我们选择重命名函数参数:
let p = 1; function myFunc(q = p + 1) { return q; } myFunc(); // => 2
函数参数从 p
重命名为 q
。当调用 myFunc()
时,未指定参数,因此将参数 q
初始化为默认值 p + 1
。为了评估 p +1
,访问外部作用域的变量 p
:p +1 = 1 + 1 = 2
if (true) { function greet() { // function body } class Greeter { // class body } } greet(); // ??? new Greeter(); // ???
message</code kann innerhalb des Bereichs von <code> zugegriffen werden if
Codeblock >. Außerhalb des Gültigkeitsbereichs ist die Variable jedoch nicht zugänglich. 🎜🎜Okay, das ist eine kurze Einführung in Scopes. Wenn Sie mehr wissen möchten, empfehle ich die Lektüre meines Artikels Erkläre die Rolle von JavaScript in einfachen Worten Domäne. 🎜🎜Hier sind 5 interessante Situationen, in denen sich der JavaScript-Bereich anders verhält als erwartet. Sie können diese Fälle studieren, um Ihr Verständnis des Umfangs zu verbessern oder sich einfach auf ein Vorstellungsgespräch vorzubereiten. 🎜l
und i
drucken? 🎜console.log(l)
gibt die Zahl 3
aus, während console.log(i )
löst ReferenceError
aus. 🎜🎜l
-Variablen werden mit der var
-Anweisung deklariert. Möglicherweise wissen Sie bereits, dass die Variable var
ist nur durch den Umfang des Funktionskörpers und nicht durch den Codeblock begrenzt. 🎜🎜Stattdessen wird die Variable i
mit der let
-Anweisung deklariert. Da die Variable let
einen Blockbereich hat, kann auf i
nur innerhalb des Schleifenbereichs for
zugegriffen werden. 🎜l
-Deklaration von var l = Colors.length
in const l = Colors geändert. Länge
. Jetzt ist die Variable l
im Rumpf der for
-Schleife eingeschlossen. 🎜hello()< / Code> Was passiert? <em>(Das Code-Snippet wird in der ES2015-Umgebung ausgeführt)</em>🎜<h4><strong>Antwort</strong></h4>🎜Da der Codeblock einen Bereich für die Funktionsdeklaration erstellt, wird er aufgerufen In der ES2015-Umgebung löst <code>hello()
ReferenceError: hello is not Defined
aus. 🎜🎜Interessanterweise wird in Umgebungen vor ES2015 kein Fehler ausgegeben, wenn der obige Codeausschnitt ausgeführt wird. Wissen Sie warum? Bitte schreiben Sie Ihre Antworten unten in die Kommentare! 🎜'import' und 'export' erscheinen möglicherweise nur auf der obersten Ebene
. 🎜🎜Sie können dies nur im obersten Bereich einer Moduldatei tun (auch bekannt als Modulbereich). 🎜import
-Anweisung am Anfang der Quelldatei zu platzieren: 🎜rrreee🎜Das Modulsystem von ES2015 ist statisch. Bestimmen Sie Modulabhängigkeiten, indem Sie den JavaScript-Quellcode analysieren, anstatt Code auszuführen. Daher können Sie keine import
-Anweisungen in Codeblöcke oder Funktionen einbinden, da diese zur Laufzeit ausgeführt werden. 🎜myFunc()
wird Was ist passiert? 🎜myFunc()
wird ein Fehler ausgegeben: ReferenceError: Cannot access 'p' before initialization < /code>. 🎜🎜Dies geschieht, weil die Parameter der Funktion ihren eigenen Geltungsbereich haben (getrennt vom Funktionsumfang). Das Argument <code>p = p + 1
ist äquivalent zu let p = p + 1
. 🎜🎜Schauen wir uns p = p + 1
genauer an. 🎜🎜Zuerst definieren Sie die Variable p
. JavaScript versucht dann, den Standardwertausdruck p + 1
auszuwerten, aber zu diesem Zeitpunkt wurde die Bindung p
erstellt, aber noch nicht initialisiert (auf Variablen des äußeren Bereichs < kann nicht zugegriffen werden code>let p = 1). Daher wird ein Fehler ausgegeben, dass auf p
vor der Initialisierung zugegriffen wurde. 🎜let p = 1
umbenennen oder den Funktionsparameter p = umbenennen p + 1
. 🎜🎜Lassen Sie uns den Funktionsparameter umbenennen: 🎜rrreee🎜Der Funktionsparameter wird von p
in q
umbenannt. Beim Aufruf von myFunc()
werden keine Parameter angegeben, daher wird der Parameter q
auf den Standardwert p + 1
initialisiert. Um p +1
auszuwerten, greifen Sie auf die Variable p
des äußeren Bereichs zu: p +1 = 1 + 1 = 2
. 🎜以下代码在代码块内定义了一个函数和一个类:
if (true) { function greet() { // function body } class Greeter { // class body } } greet(); // ??? new Greeter(); // ???
是否可以在块作用域之外访问 greet
和 Greeter
? (考虑 ES2015 环境)
function
和 class
声明都是块作用域的。所以在代码块作用域外调用函数 greet()
和构造函数 new Greeter()
就会抛出 ReferenceError
。
必须注意 var
变量,因为它们是函数作用域的,即使是在代码块中定义的。
由于 ES2015 模块系统是静态的,因此你必须在模块作用域内使用 import
语法(以及 export
)。
函数参数具有其作用域。设置默认参数值时,请确保默认表达式内的变量已经用值初始化。
在 ES2015 运行时环境中,函数和类声明是块作用域的。但是在 ES2015 之前的环境中,函数声明仅在函数作用域内。
希望这些陷阱能够帮你巩固作用域知识!
英文原文地址:https://dmitripavlutin.com/javascript-scope-gotchas/
作者:Dmitri Pavlutin
更多编程相关知识,请访问:编程入门!!
Das obige ist der detaillierte Inhalt von5 Fallstricke beim JS Scope (Zusammenfassung). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!