Pourquoi certaines variables déclarées en utilisant let inside une fonction deviennent disponibles dans une autre fonction, tandis que d'autres entraînent une erreur de référence?

158

Je ne peux pas comprendre pourquoi les variables agissent si étrangement lorsqu'elles sont déclarées dans une fonction.

  1. Dans la firstfonction je déclare avec letles variables bet cavec la valeur 10 :

    b = c = 10;

    Dans la secondfonction je montre:

    b + ", " + c

    Et cela montre:

    10, 10
  2. Toujours dans la firstfonction, je déclare aavec la valeur 10 :

    let a = b = c = 10;

    Mais dans la secondfonction, il montre une erreur:

    Impossible de trouver la variable: a

  3. Maintenant, dans la firstfonction que je déclare davec la valeur 20 :

    var d = 20;

    Mais dans la secondfonction, il affiche la même erreur que précédemment, mais avec la variable d:

    Impossible de trouver la variable: d

Exemple:

function first() {
  let a = b = c = 10;
  var d = 20;
  second();
}

function second() {
  console.log(b + ", " + c); //shows "10, 10"

  try{ console.log(a); }  // Rreference error
  catch(e){ console.error(e.message) }

  try{ console.log(d); } // Reference error
  catch(e){ console.error(e.message) }
}
first()

interférence de bêlement
la source
31
Vous déclarez des globaux, car bet cne sont pas préfixés par le varmot - clé. aet dsont locaux à first.
VLAZ
1
Les commentaires ne sont pas pour une discussion approfondie; une conversation tangentielle pour savoir si ce serait une bonne question d'entrevue a été archivée dans le chat .
Cody Gray
1
Cela me rappelle une situation similaire dans Visual Basic; Dim Apple, Banana, Pear As Fruitsignifie Dim Apple / Dim Banana / Dim Pear As Fruit, et non Dim Apple As Fruit / ....
Eric Lippert

Réponses:

179

C'est parce que vous dites en fait:

c = 10;
b = c;
let a = b;

Et pas ce que vous pensez dire, c'est-à-dire:

let a = 10;
let b = 10;
let c = 10;

Vous remarquerez que peu importe le nombre de variables que vous ajoutez à votre chaîne, ce ne sera que la première (a) qui cause l'erreur.

C'est parce que "let" étend votre variable au bloc (ou, "localement", signifiant plus ou moins "entre crochets") dans lequel vous le déclarez.

Si vous déclarez une variable sans "let", elle étend la variable globalement.

Ainsi, dans la fonction où vous définissez vos variables, tout obtient la valeur 10 (vous pouvez le voir dans le débogueur si vous mettez un point d'arrêt). Si vous mettez un journal de console pour a, b, c dans cette première fonction, tout va bien.

Mais dès que vous quittez cette fonction, la première (a) - et encore une fois, gardez à l'esprit, techniquement dans l'ordre d'affectation, c'est la dernière - "disparaît" (encore une fois, vous pouvez le voir dans le débogueur si vous définissez un point d'arrêt dans la deuxième fonction), mais les deux autres (ou le nombre que vous ajoutez) sont toujours disponibles.

En effet, "let" S'APPLIQUE UNIQUEMENT (donc uniquement aux étendues locales) LA PREMIÈRE VARIABLE - encore une fois, qui est techniquement la dernière à être déclarée et à laquelle une valeur a été attribuée - dans la chaîne. Techniquement, les autres n'ont pas "laissé" devant eux. Donc, ceux-ci sont techniquement déclarés globalement (c'est-à-dire sur l'objet global), c'est pourquoi ils apparaissent dans votre deuxième fonction.

Essayez-le: supprimez le mot clé "let". Tous vos vars seront désormais disponibles.

"var" a un effet de portée locale similaire, mais diffère dans la façon dont la variable est "hissée", ce que vous devez certainement comprendre, mais qui n'est pas directement impliqué dans votre question.

(BTW, cette question serait assez époustouflante pour les développeurs JS pro pour en faire une bonne).

Je vous suggère fortement de passer du temps avec les différences dans la façon dont les variables peuvent être déclarées dans JS: sans mot-clé, avec "let" et avec "var".

Tim Consolazio
la source
4
C'est à la fois la meilleure et la pire chose de la programmation: l'ordinateur fera exactement ce que vous lui demandez de faire. Pas nécessairement ce que vous aviez l'intention de lui dire de faire. Les programmes sont parfaits. Nous créons les problèmes.
Niet the Dark Absol le
8
@Thevs Pourquoi recommandez-vous varplus letdans le cadre de cette réponse? Je ne comprends pas.
Klaycon
4
@Thevs, je suis fortement en désaccord avec vous. varpeut être sujet à des bogues s'il est utilisé avec négligence. Vérifiez ce violon
Cid
2
@Thevs dans quel cas a-t var-il un avantage sur let? Permettez-moi de clarifier: un contexte moderne où les deux sont des options et je demande du code que l'on devrait écrire. Quand j'ai posé cette question auparavant, j'ai reçu des réponses sur "vous pouvez re-déclarer une variable avec var", auquel cas je dois rappeler aux gens que vous ne devriez pas redéclarer des variables . C'est soit un bug, soit une erreur dans la logique du code - donc l'avantage de la re-déclaration est ... qu'elle vous permet d'écrire du code défectueux. Je n'ai pas encore vu de raison valable en faveur de varquand letest également une option.
VLAZ
2
Marquez une autre marque contre javascript. Toutes les variables sont globales sauf si déclarées locales. :(
JRE
68

Dans la fonction first(), les variables bet csont créées à la volée, sans utiliser varou let.

let a = b = c = 10; // b and c are created on the fly

Est différent de

let a = 10, b = 10, c = 10; // b and c are created using let (note the ,)

Ils deviennent implicitement mondiaux. C'est pourquoi ils sont disponibles ensecond()

De la documentation

L'affectation d'une valeur à une variable non déclarée la crée implicitement en tant que variable globale (elle devient une propriété de l'objet global) lors de l'exécution de l'affectation.

Pour éviter cela, vous pouvez utiliser "use strict"qui fournira des erreurs lorsque l'on utilise une variable non déclarée

"use strict"; // <-------------- check this

function first() {
   /*
    * With "use strict" c is not defined.
    * (Neither is b, but since the line will be executed from right to left,
    * the variable c will cause the error and the script will stop)
    * Without, b and c become globals, and then are accessible in other functions
    */
   let a = b = c = 10;
   var d = 20;
   second();
}

function second() {
   console.log(b + ", " + c); //reference error
   console.log(a); //reference error
   console.log(d); //reference error
}

first();

Cid
la source
15
De plus: let a = 10, b = 10, c = 10;ou let a, b, c; a = b = c = 10;serait autrement une façon correcte de déclarer les variables.
Rickard Elimää
Donc avec le mode strict, qu'en est-il de la variable b?
Tick20
2
@ Tick20 la variable bne sera pas évaluée / atteinte, l'erreur se produira sur la ligne let a = b = c = 10;, lue de droite à gauche . cétant la première variable à l'origine ReferenceError, le reste de la ligne ne sera pas exécuté (le script s'est arrêté)
Cid
2
quelque chose comme let a = 10, b = a, c = b;est aussi valable
Kaddath
8
voté principalement pour le "strict usage". Dans le cadre d'une question d'entretien, ce serait aussi aussi le début de mon commentaire sur ce code.
Pac0
23

Avant d'appeler des choses étranges, connaissons d'abord quelques notions de base:

var et let sont tous deux utilisés pour la déclaration de variables en JavaScript. Par exemple,

var one = 1;
let two = 2;

Les variables peuvent également être déclarées sans utiliser varou let. Par exemple,

three = 3;

Maintenant, la différence entre les approches ci-dessus est que:

var est fonction portée

et

let a une portée de bloc.

tandis que la portée des variables déclarées sans var/ letmot - clé devient globale quel que soit l'endroit où elle est déclarée.

Les variables globales sont accessibles de n'importe où dans la page Web (non recommandé car les globales peuvent être modifiées accidentellement).

Maintenant, selon ces concepts, regardons le code en question:

 function first() {
   let a = b = c = 10;
   /* The above line means:
    let a=10; // Block scope
    b=10; // Global scope
    c=10; // Global scope
    */

   var d = 20; // Function scope
   second();
}

function second() {
   alert(b + ", " + c); // Shows "10, 10" //accessible because of global scope
   alert(a); // Error not accessible because block scope has ended
   alert(d); // Error not accessible because function scope has ended
}
fatimasajjad
la source
1
Vous avez plagié une partie de la réponse de JonoJames . Pourquoi?
Peter Mortensen
2
Je suis désolé mais je n'avais pas une telle intention, quelque chose de similaire pourrait être là parce que nous pourrions avoir collecté une information de la même source.
fatimasajjad
2
Il se peut que les deux réponses contiennent du contenu copié à partir de la même source d'origine sans citation apparente - éventuellement tutorialsteacher.com/javascript/javascript-variable . La présence de plagiat est apparente, car une erreur grammaticale est reproduite à partir de l'original - "la portée des variables déclarées sans le varmot - clé devient globale indépendamment de l'endroit où elle est déclarée" devrait être soit "la portée ... devient" ou "la portées ... deviennent " . Utiliser les mots exacts de quelqu'un d'autre nécessite une citation, que ce soit d'ici ou d'ailleurs. meta.stackexchange.com/q/160071/211183
Michael - sqlbot
Merci les gars, j'ai ajouté un lien de référence pour la source.
fatimasajjad
6

Les variables utilisant le letmot - clé ne doivent être disponibles que dans le cadre du bloc et non disponibles dans une fonction externe ...

Chaque variable que vous déclarez de cette manière n'utilise pas letou var. Il manque une virgule dans la déclaration des variables.

Il n'est pas recommandé de déclarer une variable sans le varmot clé. Il peut accidentellement remplacer une variable globale existante. La portée des variables déclarées sans le varmot clé devient globale quel que soit l'endroit où il est déclaré. Les variables globales sont accessibles de n'importe où dans la page Web.

function first() {
   let a = 10;
   let b = 10;
   let c = 10;
   var d = 20;
   second();
}

function second() {
   console.log(b + ", " + c); //shows "10, 10"
   console.log(a); //reference error
   console.log(d); //reference error
}

first();

JonoJames
la source
3

C'est parce que vous n'utilisez pas letou que la varvariable est déclarée à la volée, mieux vous déclarez comme suit.

let a = 10;
let b = 10;
let c = 10;
Mac Rathod
la source
2

L'étrange problème est dû aux règles de portée en JavaScript

function first() {
   let a = b = c = 10; // a is in local scope, b and c are in global scope
   var d = 20; // d is in local scope
   second(); // will have access to b and c from the global scope
}

En supposant que vous souhaitez déclarer 3 variables locales initialisées à la même valeur (100). Votre premier () ressemblera à ci-dessous. Dans ce cas, second () n'aura accès à aucune des variables car elles sont locales à first ()

function first() {
   let a = 100; // a is in local scope init to 100
   let b = a; // b is in local scope init to a
   let c = b // c is in local scope init to b

   var d = 20; // d is in local scope
   second(); // will not have access a, b, c, or d
}

Cependant, si vous voulez des variables globales, votre premier () ressemblera à ci-dessous. Dans ce cas, second aura accès à toutes les variables car elles sont de portée globale

function first() {
   a = 100; // a is in global scope
   b = a; // b is in global scope
   c = b // c is in global scope

   d = 20; // d is in global scope
   second(); // will have access to a, b, c, and d from the global scope
}

Variables locales (aka. Accessibles dans le bloc de code où elles sont déclarées).
Un bloc de code est un {} avec une ou plusieurs lignes de code entre.

  • function () {var, let, const ici est accessible à la fonction entière},
  • for () {var in here is accessible to external scope, let, const accessible only in here},
  • etc.

Variables globales (alias accessibles dans la portée globale).
Ces variables sont attachées à l'objet global. L'objet global dépend de l'environnement. C'est l'objet fenêtre dans les navigateurs.

Remarque spéciale: vous pouvez déclarer des variables en JavaScript sans utiliser les mots clés var, let, const. Une variable déclarée de cette façon est attachée à l'objet global, donc accessible dans la portée globale.
a = 100 // is valid and is in global scope

Quelques articles pour plus de lecture: https://www.sitepoint.com/demystifying-javascript-variable-scope-hoisting/ https://scotch.io/tutorials/understanding-scope-in-javascript https: //www.digitalocean .com / communauté / tutoriels / comprehension-variables-scope-hoisting-in-javascript

Funwie
la source
0

La principale différence réside dans les règles de portée. Les variables déclarées par le mot clé var sont étendues au corps de la fonction immédiate (d'où l'étendue de la fonction) tandis que les variables let sont étendues au bloc englobant immédiat désigné par {} (d'où l'étendue du bloc). Et quand tu dis

c = 10;
b = c;
let a = b;

c et b ont la durée de vie aussi amusante que la durée d'un bloc et si vous essayez d'accéder à a en la référençant, il affiche toujours une erreur mais c et b sont globalement donc ils ne le font pas. variables que vous ajoutez à votre chaîne, ce ne sera que la première (a) qui provoque l'erreur. C'est parce que "laisser" étend votre variable au bloc (ou, "localement", plus ou moins signifiant "entre crochets") dans lequel vous la déclarez. Si vous déclarez une variable sans "let", elle étend la variable globalement. Donc, dans la fonction où vous définissez vos variables, tout obtient la valeur 10 (vous pouvez le voir dans le débogueur si vous mettez un point d'arrêt). Si vous mettez un journal de console pour a, b, c dans cette première fonction, tout va bien, mais dès que vous quittez cette fonction, la première (a) - et encore une fois, gardez à l'esprit,

Muhammad Fahad
la source
0

Voici les 3 aspects intéressants des déclarations de variables en JavaScript:

  1. var restreint la portée de la variable au bloc dans lequel elle est définie. ( 'var' est pour la portée locale .)

  2. let permet de remplacer temporairement la valeur d'une variable externe à l'intérieur d'un bloc.

  3. La simple déclaration d'une variable sans var ou let rendra la variable globale, quel que soit l'endroit où elle est déclarée.

Voici une démo de let , qui est le dernier ajout à la langue:

// File name:  let_demo.js

function first() {
   a = b = 10
   console.log("First function:    a = " + a)
   console.log("First function:    a + b = " + (a + b))
}

function second() {
    let a = 5
    console.log("Second function:    a = " + a)
    console.log("Second function:    a + b = " + (a + b))
}

first()   

second()

console.log("Global:    a = " + a)
console.log("Global:    a + b = " + (a + b))

Production:

$ node let_demo.js 

First function:    a = 10
First function:    a + b = 20

Second function:    a = 5
Second function:    a + b = 15

Global:    a = 10
Global:    a + b = 20

Explication:

Les variables a et b ont été supprimées à l'intérieur de ' first () ', sans mots-clés var ou let.

Par conséquent, a et b sont globaux et sont donc accessibles tout au long du programme.

Dans la fonction nommée 'second' , l'instruction 'let a = 5' définit temporairement la valeur de ' a ' à ' 5 », dans le cadre de la fonction uniquement.

En dehors de la portée de « second () », IE, dans la portée globale, la valeur de « a » sera telle que définie précédemment.

Gopinath
la source