une - variable javascript




Quelle est la portée des variables dans JavaScript? (17)

Js modernes, ES6 +, ' const ' et ' let '

Vous devriez utiliser la portée de bloc pour chaque variable que vous créez, comme la plupart des autres langues principales. var est obsolète . Cela rend votre code plus sûr et plus facile à maintenir.

const devrait être utilisé pour 95% des cas . Cela fait que la référence de la variable ne peut pas changer. Les propriétés de tableau, d'objet et de noeud DOM peuvent changer et devraient probablement être const .

let devrait être utilisé pour toute variable attendant d'être réaffectée. Cela inclut dans une boucle for. Si vous changez de valeur au-delà de l'initialisation, utilisez let .

La portée de bloc signifie que la variable ne sera disponible qu'à l'intérieur des parenthèses dans lesquelles elle est déclarée. Cela s'étend aux étendues internes, y compris les fonctions anonymes créées dans votre périmètre.

Quelle est la portée des variables en javascript? Ont-ils la même portée à l'intérieur plutôt qu'à l'extérieur d'une fonction? Ou est-ce important? De même, où sont stockées les variables si elles sont définies globalement?


Vieille école JavaScript

Traditionnellement, JavaScript n'a vraiment que deux types de portée:

  1. Global Scope : Les variables sont connues dans toute l'application, depuis le début de l'application (*)
  2. Functional Scope : Les variables sont connues dans la fonction dans laquelle elles sont déclarées, depuis le début de la fonction (*)

Je ne vais pas m'étendre là-dessus, car il y a déjà beaucoup d'autres réponses expliquant la différence.

JavaScript moderne

Les spécifications JavaScript les plus récentes permettent maintenant une troisième portée:

  1. Bloc Scope : Les variables sont connues dans le bloc dans lequel elles sont déclarées, à partir du moment où elles sont déclarées (**)

Comment créer des variables de portée de bloc?

Traditionnellement, vous créez vos variables comme ceci:

var myVariable = "Some text";

Les variables d'étendue de bloc sont créées comme ceci:

let myVariable = "Some text";

Alors, quelle est la différence entre la portée fonctionnelle et la portée du bloc?

Pour comprendre la différence entre la portée fonctionnelle et la portée de bloc, considérez le code suivant:

// i IS NOT known here
// j IS NOT known here
// k IS known here, but undefined
// l IS NOT known here

function loop(arr) {
    // i IS known here, but undefined
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( var i = 0; i < arr.length; i++ ) {
        // i IS known here, and has a value
        // j IS NOT known here
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( let j = 0; j < arr.length; j++ ) {
        // i IS known here, and has a value
        // j IS known here, and has a value
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here
}

loop([1,2,3,4]);

for( var k = 0; k < arr.length; k++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS NOT known here
};

for( let l = 0; l < arr.length; l++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS known here, and has a value
};

loop([1,2,3,4]);

// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here

Ici, nous pouvons voir que notre variable j n'est connue que dans la première boucle, mais pas avant et après. Pourtant, notre variable i est connue dans toute la fonction.

En outre, considérez que les variables de portée de bloc ne sont pas connues avant d'être déclarées car elles ne sont pas levées. Vous n'êtes pas non plus autorisé à redéclarer la même variable étendue de bloc dans le même bloc. Cela rend les variables à portée de bloc moins sujettes aux erreurs que les variables à portée globale ou fonctionnelle, qui sont hissées et qui ne produisent pas d'erreurs dans le cas de déclarations multiples.

Est-il sûr d'utiliser des variables de portée de bloc aujourd'hui?

Qu'il soit ou non sûr à utiliser aujourd'hui, dépend de votre environnement:

  • Si vous écrivez du code JavaScript côté serveur ( Node.js ), vous pouvez utiliser l'instruction let toute sécurité.

  • Si vous écrivez du code JavaScript côté client et que vous utilisez un transpileur (comme Traceur ), vous pouvez utiliser l'instruction let toute sécurité, mais votre code risque d'être tout sauf optimal en termes de performances.

  • Si vous écrivez du code JavaScript côté client et n'utilisez pas de transpiler, vous devez prendre en compte le support du navigateur.

    Aujourd'hui, le 23 février 2016, voici quelques navigateurs qui ne supportent pas ou qui ne supportent que partiellement:

    • Internet Explorer 10 et ci-dessous (pas de support)
    • Firefox 43 et ci-dessous (pas de support)
    • Safari 9 et ci-dessous (pas de support)
    • Opera Mini 8 et ci-dessous (pas de support)
    • Navigateur Android 4 et ci-dessous (pas de support)
    • Opera 36 et ci-dessous (support partiel)
    • Chome 51 et ci-dessous (support partiel)

Comment suivre le support du navigateur

Pour obtenir une présentation à jour des navigateurs prenant en charge l'instruction let au moment de la lecture de cette réponse, consultez cette page Can I Use ?

(*) Les variables globales et fonctionnelles peuvent être initialisées et utilisées avant d'être déclarées car les variables JavaScript sont hoisted . Cela signifie que les déclarations sont toujours très en haut de la portée.

(**) Les variables de portée de bloc ne sont pas hissées


1) Il existe une portée globale, une portée de fonction et les étendues with et catch. Il n'y a pas de portée de niveau 'bloc' en général pour les variables - les instructions with et catch ajoutent des noms à leurs blocs.

2) Les étendues sont imbriquées par des fonctions jusqu'à la portée globale.

3) Les propriétés sont résolues en passant par la chaîne prototype. L'instruction with apporte les noms de propriété d'objet dans la portée lexicale définie par le bloc with.

EDIT: ECMAAScript 6 (Harmony) est spec'ed pour soutenir let, et je sais que le chrome permet un drapeau "d'harmonie", alors peut-être qu'il le supporte.

Supposons que vous utilisiez le mot-clé au niveau du bloc, mais vous devez utiliser le mot-clé pour y arriver.

EDIT: Sur la base des remarques de Benjamin sur les déclarations with et catch dans les commentaires, j'ai modifié le post, et ajouté plus. Les instructions with et catch introduisent des variables dans leurs blocs respectifs, c'est-à- dire une portée de bloc. Ces variables sont aliasées aux propriétés des objets qui leur sont transmis.

 //chrome (v8)

 var a = { 'test1':'test1val' }
 test1   // error not defined
 with (a) { var test1 = 'replaced' }
 test1   // undefined
 a       // a.test1 = 'replaced'

EDIT: Exemple de clarification:

test1 est étendu au bloc with, mais est aliasé par a.test1. 'Var test1' crée une nouvelle variable test1 dans le contexte lexical supérieur (function, ou global), sauf s'il s'agit d'une propriété de - ce qu'elle est.

Yikes! Soyez prudent en utilisant 'avec' - tout comme var est un noop si la variable est déjà définie dans la fonction, c'est aussi un noop par rapport aux noms importés de l'objet! Un peu de tête sur le nom déjà défini rendrait cela beaucoup plus sûr. Personnellement, je ne l'utiliserai jamais à cause de cela.


Chaque segment de code JavaScript (code global ou fonctions) est associé à une chaîne de domaine. Cette chaîne de portée est une liste ou une chaîne d'objets qui définit les variables "dans la portée" de ce code. Lorsque JavaScript doit rechercher la valeur d'une variable x (un processus appelé résolution variable ), il commence par regarder le premier objet de la chaîne. Si cet objet a une propriété nommée x , la valeur de cette propriété est utilisée. Si le premier objet n'a pas de propriété nommée x , JavaScript poursuit la recherche avec l'objet suivant dans la chaîne. Si le second objet n'a pas de propriété nommée x , la recherche passe à l'objet suivant, et ainsi de suite. Si x n'est pas une propriété de l'un des objets de la chaîne de portée, alors x n'est pas dans la portée de ce code et une erreur ReferenceError se produit. Dans le code JavaScript de niveau supérieur (c'est-à-dire, le code ne figure dans aucune définition de fonction), la chaîne de portée est constituée d'un seul objet, l'objet global. Dans une fonction non imbriquée, la chaîne de portée est constituée de deux objets. Le premier est l'objet qui définit les paramètres de la fonction et les variables locales, et le second est l'objet global. Dans une fonction imbriquée, la chaîne de portée a trois objets ou plus. Il est important de comprendre comment cette chaîne d'objets est créée. Lorsqu'une fonction est DEFINIE , elle stocke la chaîne de portée alors en vigueur. Lorsque cette fonction est INVOKED , elle crée un nouvel objet pour stocker ses variables locales et ajoute ce nouvel objet à la chaîne de portée stockée pour créer une nouvelle chaîne, plus longue, représentant l'étendue de l'appel de cette fonction. Cela devient plus intéressant pour les fonctions imbriquées car chaque fois que la fonction externe est appelée, la fonction interne est à nouveau définie. Since the scope chain differs on each invocation of the outer function, the inner function will be subtly different each time it is defined—the code of the inner function will be identical on each invocation of the outer function, but the scope chain associated with that code will be different . This notion of a scope chain is crucial for understanding closures .


Il y a presque deux types d'étendues JavaScript:

  • la portée de chaque déclaration var est associée à la fonction englobante la plus immédiate
  • s'il n'y a pas de fonction englobante pour une déclaration var, c'est une portée globale

Ainsi, tous les blocs autres que les fonctions ne créent pas de nouvelle portée. Cela explique pourquoi les boucles for-over écrasent les variables de portée externes:

var i = 10, v = 10;
for (var i = 0; i < 5; i++) { var v = 5; }
console.log(i, v);
// output 5 5

En utilisant des fonctions à la place:

var i = 10, v = 10;
$.each([0, 1, 2, 3, 4], function(i) { var v = 5; });
console.log(i,v);
// output 10 10

Dans le premier exemple, il n'y avait pas de portée de bloc, donc les variables initialement déclarées ont été écrasées. Dans le second exemple, il y avait une nouvelle portée due à la fonction, donc les variables initialement déclarées étaient SHADOWED, et non écrasées.

C'est presque tout ce que vous devez savoir en termes de portée JavaScript, sauf:

Donc, vous pouvez voir que le scoping JavaScript est en réalité extrêmement simple, bien que pas toujours intuitif. Quelques points à connaître:

  • Les déclarations var sont hissées en haut de la portée. Cela signifie que peu importe où la déclaration var se produit, pour le compilateur, c'est comme si le var se produisait en haut
  • plusieurs déclarations var dans la même portée sont combinées

Donc ce code:

var i = 1;
function abc() {
  i = 2;
  var i = 3;
}
console.log(i);     // outputs 1

est équivalent à:

var i = 1;
function abc() {
  var i;     // var declaration moved to the top of the scope
  i = 2;
  i = 3;     // the assignment stays where it is
}
console.log(i);

Cela peut sembler contre-intuitif, mais cela a du sens du point de vue d'un concepteur de langage impératif.


Il y a seulement des étendues de fonction dans JS. Ne pas bloquer les étendues! Vous pouvez voir ce qui est hisser aussi.

var global_variable = "global_variable";
var hoisting_variable = "global_hoist";

// Global variables printed
console.log("global_scope: - global_variable: " + global_variable);
console.log("global_scope: - hoisting_variable: " + hoisting_variable);

if (true) {
    // The variable block will be global, on true condition.
    var block = "block";
}
console.log("global_scope: - block: " + block);

function local_function() {
    var local_variable = "local_variable";
    console.log("local_scope: - local_variable: " + local_variable);
    console.log("local_scope: - global_variable: " + global_variable);
    console.log("local_scope: - block: " + block);
    // The hoisting_variable is undefined at the moment.
    console.log("local_scope: - hoisting_variable: " + hoisting_variable);

    var hoisting_variable = "local_hoist";
    // The hoisting_variable is now set as a local one.
    console.log("local_scope: - hoisting_variable: " + hoisting_variable);
}

local_function();

// No variable in a separate function is visible into the global scope.
console.log("global_scope: - local_variable: " + local_variable);

JavaScript n'a que deux types de portée:

  1. Global Scope : Global n'est rien d'autre qu'une portée de niveau fenêtre. Ici, variable présente dans toute l'application.
  2. Portée fonctionnelle : La variable déclarée dans une fonction avec un mot-clé var a une portée fonctionnelle.

Chaque fois qu'une fonction est appelée, un objet à portée variable est créé (et inclus dans la chaîne de portée) qui est suivi de variables dans JavaScript.

        a = "global";
         function outer(){ 
              b = "local";
              console.log(a+b); //"globallocal"
         }
outer();

Chaîne de portée ->

  1. Le niveau de la fenêtre - a et outer fonction outer sont au niveau supérieur de la chaîne de portée.
  2. quand la fonction externe a appelé un nouvel variable scope object (et inclus dans la chaîne de portée) ajouté avec la variable b intérieur.

Maintenant, quand une variable a requis, elle recherche d'abord la portée variable la plus proche et si la variable n'est pas là, elle se déplace vers l'objet suivant de la chaîne variable. Dans ce cas, c'est le niveau de la fenêtre.


Javascript utilise des chaînes de portée pour établir la portée d'une fonction donnée. Il existe généralement une portée globale et chaque fonction définie possède sa propre portée imbriquée. Toute fonction définie dans une autre fonction a une portée locale qui est liée à la fonction externe. C'est toujours la position dans la source qui définit la portée.

Un élément de la chaîne de portée est essentiellement une carte avec un pointeur vers sa portée parente.

Lors de la résolution d'une variable, javascript commence à la portée la plus interne et effectue une recherche vers l'extérieur.


Juste pour ajouter aux autres réponses, scope est une liste de recherche de tous les identifiants déclarés (variables), et applique un ensemble de règles strictes quant à la façon dont elles sont accessibles au code en cours d'exécution. Cette recherche peut avoir pour but d'assigner à la variable, qui est une référence LHS (à gauche), ou peut être dans le but de récupérer sa valeur, qui est une référence RHS (à droite). Ces recherches sont ce que le moteur JavaScript effectue en interne lorsqu'il compile et exécute le code.

Donc, de ce point de vue, je pense qu'une image serait utile dans le livre Scopes and Closures de Kyle Simpson:

Citant de son ebook:

Le bâtiment représente le jeu de règles de portée imbriqué de notre programme. Le premier étage du bâtiment représente votre portée en cours d'exécution, où que vous soyez. Le niveau supérieur du bâtiment est la portée globale. Vous résolvez les références LHS et RHS en regardant sur votre étage actuel, et si vous ne le trouvez pas, en prenant l'ascenseur à l'étage suivant, en regardant là, puis le suivant, et ainsi de suite. Une fois que vous arrivez au dernier étage (la portée globale), vous trouvez ce que vous cherchez, ou vous ne trouvez pas ce que vous cherchez. Mais vous devez vous arrêter quoi qu'il arrive.

Une chose importante à noter: "La recherche de portée s'arrête dès qu'elle trouve le premier match".

Cette idée de "niveaux d'étendue" explique pourquoi "ceci" peut être modifié avec une portée nouvellement créée, si elle est recherchée dans une fonction imbriquée. Voici un lien qui va dans tous ces détails, Tout ce que vous vouliez savoir sur la portée de javascript


L'idée de scoping en JavaScript à l'origine conçu par Brendan Eich provenait du langage de script HyperCard HyperTalk .

Dans cette langue, les affichages étaient similaires à une pile de fiches. Il y avait une carte maîtresse appelée arrière-plan. C'était transparent et peut être vu comme la carte du bas. Tout le contenu de cette carte de base a été partagé avec des cartes placées dessus. Chaque carte placée sur le dessus avait son propre contenu qui avait la priorité sur la carte précédente, mais avait toujours accès aux cartes précédentes si désiré.

C'est exactement ainsi que le système de portée JavaScript est conçu. Il a juste des noms différents. Les cartes en JavaScript sont connues sous le nom de Contextes d'exécution ECMA . Chacun de ces contextes contient trois parties principales. Un environnement variable, un environnement lexical et une liaison de ce type. Pour en revenir à la référence des cartes, l'environnement lexical contient tout le contenu des cartes précédentes plus bas dans la pile. Le contexte actuel est en haut de la pile et tout contenu déclaré sera stocké dans l'environnement variable. L'environnement variable aura préséance dans le cas de collisions de nommage.

Cette liaison pointe vers l'objet conteneur. Parfois, les étendues ou les contextes d'exécution changent sans que l'objet conteneur ne change, comme dans une fonction déclarée où l'objet conteneur peut être une window ou une fonction constructeur.

Ces contextes d'exécution sont créés à tout moment où le contrôle est transféré. Le contrôle est transféré lorsque le code commence à s'exécuter, principalement à partir de l'exécution de la fonction.

Donc, c'est l'explication technique. En pratique, il est important de se souvenir qu'en JavaScript

  • Les portées sont techniquement des "Contextes d'exécution"
  • Les contextes forment une pile d'environnements dans lesquels les variables sont stockées
  • Le haut de la pile a la priorité (le bas étant le contexte global)
  • Chaque fonction crée un contexte d'exécution (mais pas toujours une nouvelle cette liaison)

En appliquant ceci à l'un des exemples précédents (5. "Fermeture") sur cette page, il est possible de suivre la pile de contextes d'exécution. Dans cet exemple, il y a trois contextes dans la pile. Ils sont définis par le contexte externe, le contexte dans la fonction invoquée immédiatement par var six, et le contexte dans la fonction retournée à l'intérieur de la fonction invoquée immédiatement par var six.

i ) Le contexte externe. Il a un environnement variable de a = 1
ii ) Le contexte IIFE, il a un environnement lexical de a = 1, mais un environnement variable de a = 6 qui a la priorité dans la pile
iii ) Le contexte de la fonction retournée, il a un environnement lexical de a = 6 et c'est la valeur référencée dans l'alerte lorsqu'il est appelé.


Les variables déclarées globalement ont une portée globale. Les variables déclarées dans une fonction sont limitées à cette fonction et les variables globales d'ombre du même nom.

(Je suis sûr qu'il y a beaucoup de subtilités que de vrais programmeurs JavaScript pourront mettre en évidence dans d'autres réponses.) En particulier, je suis tombé sur cette page à propos de ce que this signifie à tout moment. bien que.)


Voici un exemple:

<script>

var globalVariable = 7; //==window.globalVariable

function aGlobal( param ) { //==window.aGlobal(); 
                            //param is only accessible in this function
  var scopedToFunction = {
    //can't be accessed outside of this function

    nested : 3 //accessible by: scopedToFunction.nested
  };

  anotherGlobal = {
    //global because there's no `var`
  }; 

}

</script>

Vous aurez envie d'enquêter sur les fermetures, et comment les utiliser pour faire des membres privés .


ECMAScript 6 introduced the let and const keywords. These keywords can be used in place of the var keyword. Contrary to the var keyword, the let and const keywords support the declaration of local scope inside block statements.

var x = 10
let y = 10
const z = 10
{
  x = 20
  let y = 20
  const z = 20
  {
    x = 30
    // x is in the global scope because of the 'var' keyword
    let y = 30
    // y is in the local scope because of the 'let' keyword
    const z = 30
    // z is in the local scope because of the 'const' keyword
    console.log(x) // 30
    console.log(y) // 30
    console.log(z) // 30
  }
  console.log(x) // 30
  console.log(y) // 20
  console.log(z) // 20
}

console.log(x) // 30
console.log(y) // 10
console.log(z) // 10

Global: variable declared outside of a function

Local: variable declared inside a function, and can only be called in that scope


In JavaScript there are two types of scope:

  • Local scope
  • Global scope

The Below function has a local scope variable carName . And this variable is not accessible from outside of the function.

function myFunction() {
    var carName = "Volvo";
    alert(carName);
    // code here can use carName
}

The Below Class has a Global scope variable carName . And this variable is accessible from everywhere in the class.

class {

    var carName = " Volvo";

    // code here can use carName

    function myFunction() {
        alert(carName);
        // code here can use carName 
    }
}

My understanding is that there are 3 scopes: global scope, available globally; local scope, available to an entire function regardless of blocks; and block scope, only available to the block, statement, or expression on which it was used. Global and local scope are indicated with the keyword 'var', either within a function or outside, and block scope is indicated with the keyword 'let'.

For those that believe there is only global and local scope, please explain why Mozilla would have an entire page describing the nuances of block scope in JS.

let


Try this curious example. In the example below if a were a numeric initialized at 0, you'd see 0 and then 1. Except a is an object and javascript will pass f1 a pointer of a rather than a copy of it. The result is that you get the same alert both times.

var a = new Date();
function f1(b)
{
    b.setDate(b.getDate()+1);
    alert(b.getDate());
}
f1(a);
alert(a.getDate());




scope