En JavaScript, un bloc de code, une fonction ou un module crée une portée pour une variable. Par exemple, le bloc de code if
crée une portée pour la variable message
:
if (true) { const message = 'Hello'; console.log(message); // 'Hello' } console.log(message); // throws ReferenceError
peut accéder à if
dans la portée du bloc de code message
. Mais en dehors du périmètre, la variable n'est pas accessible.
D'accord, c'est une brève introduction aux oscilloscopes. Si vous souhaitez en savoir plus, je vous recommande de lire mon article Portée JavaScript expliquée en mots simples .
Voici 5 situations intéressantes dans lesquelles la portée JavaScript se comporte différemment de ce à quoi vous vous attendez. Vous pouvez étudier ces cas pour améliorer votre compréhension de la portée ou simplement pour vous préparer à un entretien.
Considérez l'extrait de code suivant :
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); // ???
Que se passe-t-il lorsque vous imprimez les variables l
et i
?
console.log(l)
imprime le numéro 3
pendant que console.log(i)
lance ReferenceError
. Les variables
l
sont déclarées à l'aide de l'instruction var
. Comme vous le savez peut-être déjà, les var
variables ne sont limitées que par la portée du corps de la fonction et non par le bloc de code.
En revanche, la variable i
est déclarée à l'aide de l'instruction let
. Étant donné que la variable let
a une portée de bloc, i
n'est accessible que dans la portée de la boucle for
.
en changeant la déclaration l
de var l = colors.length
à const l = colors.length
. Maintenant, la variable l
est encapsulée dans le corps de la boucle for
.
Dans l'extrait de code suivant : Que se passe-t-il si
// ES2015 env { function hello() { return 'Hello!'; } } hello(); // ???
appelle hello()
? (L'extrait de code est exécuté dans l'environnement ES2015)
Étant donné que le bloc de code crée la portée de la déclaration de fonction, il est dans l'environnement ES2015 L'appel de hello()
augmentera ReferenceError: hello is not defined
.
Fait intéressant, dans les environnements antérieurs à ES2015, aucune erreur n'est générée lors de l'exécution de l'extrait de code ci-dessus. Savez-vous pourquoi ? Veuillez écrire vos réponses dans les commentaires ci-dessous !
Pouvez-vous importer des modules dans des blocs de code ?
if (true) { import { myFunc } from 'myModule'; // ??? myFunc(); }
Le script ci-dessus déclenchera l'erreur : 'import' and 'export' may only appear at the top-level
.
Vous ne pouvez importer des modules que dans la portée de niveau supérieur du fichier de module (également appelée portée du module).
Toujours importer les modules à partir de la portée du module. Une autre bonne pratique consiste à placer l'instruction import
au début du fichier source :
import { myFunc } from 'myModule'; if (true) { myFunc(); }
Le système de modules d'ES2015 est statique. Déterminez les dépendances des modules en analysant le code source JavaScript plutôt qu'en exécutant du code. Vous ne pouvez donc pas inclure d'instructions import
dans des blocs de code ou des fonctions car elles sont exécutées au moment de l'exécution.
Pensez à la fonction suivante :
let p = 1; function myFunc(p = p + 1) { return p; } myFunc(); // ???
Que se passe-t-il lorsque vous appelez myFunc()
?
Lors de l'appel de la fonction myFunc()
, une erreur sera générée : ReferenceError: Cannot access 'p' before initialization
.
Cela se produit car les paramètres de la fonction ont leur propre portée (distincte de la portée de la fonction). Le paramètre p = p + 1
est équivalent à let p = p + 1
.
Regardons de plus près p = p + 1
.
Tout d'abord, définissez la variable p
. JavaScript essaie ensuite d'évaluer l'expression de valeur par défaut p + 1
, mais à ce stade, la liaison p
a été créée mais pas encore initialisée (impossible d'accéder aux variables dans la portée externe let p = 1
). Par conséquent, une erreur est générée indiquant que p
a été accédé avant l'initialisation.
Pour résoudre ce problème, vous pouvez renommer les variables let p = 1
et également renommer les paramètres de fonction p = p + 1
.
Choisissons de renommer le paramètre de fonction :
let p = 1; function myFunc(q = p + 1) { return q; } myFunc(); // => 2
Le paramètre de fonction est renommé de p
en q
. Lorsque myFunc()
est appelé, aucun argument n'est spécifié, donc l'argument q
est initialisé à la valeur par défaut p + 1
. Pour évaluer p +1
, accédez à la variable p
de la portée externe : 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
更多编程相关知识,请访问:编程入门!!
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!