Por que algumas variáveis ​​declaradas usando deixar dentro de uma função se tornam disponíveis em outra função, enquanto outras resultam em um erro de referência?

158

Não consigo entender por que variáveis ​​agem tão estranhas quando declaradas dentro de uma função.

  1. Na firstfunção declaro com letas variáveis be ccom o valor 10 :

    b = c = 10;

    Na secondfunção eu mostro:

    b + ", " + c

    E isso mostra:

    10, 10
  2. Também na firstfunção declaro acom o valor 10 :

    let a = b = c = 10;

    Mas na secondfunção mostra um erro:

    Não é possível encontrar a variável: a

  3. Agora, na firstfunção, declaro dcom o valor 20 :

    var d = 20;

    Mas na secondfunção mostra o mesmo erro de antes, mas com a variável d:

    Não é possível encontrar a variável: d

Exemplo:

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()

intérprete de balidos
fonte
31
Você está declarando globals, uma vez be cnão são prefixados com o varpalavra-chave. ae dsão locais para first.
VLAZ 27/01
11
Comentários não são para discussão prolongada; uma conversa tangencial sobre se essa seria uma boa pergunta de entrevista foi arquivada no bate-papo .
Cody Gray
11
Isso me lembra uma situação semelhante no Visual Basic; Dim Apple, Banana, Pear As Fruitsignifica Dim Apple / Dim Banana / Dim Pear As Fruite não Dim Apple As Fruit / ....
Eric Lippert

Respostas:

179

É porque você está realmente dizendo:

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

E não o que você pensa que está dizendo, que é:

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

Você notará que, não importa quantas variáveis ​​você adicione à sua cadeia, será apenas a primeira (a) que causará o erro.

Isso ocorre porque "let" coloca sua variável no bloco (ou "localmente", mais ou menos significa "entre colchetes") no qual você a declara.

Se você declarar uma variável sem "let", ela escopo a variável globalmente.

Portanto, na função em que você define suas variáveis, tudo obtém o valor 10 (você pode ver isso no depurador se colocar um ponto de interrupção). Se você colocar um log do console para a, b, c nessa primeira função, tudo estará bem.

Mas assim que você sai dessa função, a primeira (a) - e novamente, lembre-se, tecnicamente na ordem de atribuição, é a última - "desaparece" (novamente, você pode ver isso no depurador se você definir um ponto de interrupção na segunda função), mas os outros dois (ou quantos você adicionar) ainda estarão disponíveis.

Isso ocorre porque "let" APLICA-SE APENAS A (portanto, somente escopos locais) A PRIMEIRA VARIÁVEL - novamente, que é tecnicamente a última a ser declarada e atribuída um valor - na cadeia. O resto tecnicamente não tem "let" na frente deles. Portanto, eles são tecnicamente declarados globalmente (ou seja, no objeto global), e é por isso que eles aparecem na sua segunda função.

Experimente: remova a palavra-chave "let". Todos os seus vars estarão agora disponíveis.

"var" tem um efeito de escopo local semelhante, mas difere em como a variável é "içada", o que é algo que você definitivamente deve entender, mas que não está diretamente envolvido com sua pergunta.

(BTW, essa pergunta seria inútil para desenvolvedores JS profissionais o suficiente para torná-la uma boa).

É altamente recomendável que você gaste tempo com as diferenças de como as variáveis ​​podem ser declaradas em JS: sem uma palavra-chave, com "let" e "var".

Tim Consolazio
fonte
4
É simultaneamente a melhor e a pior coisa da programação: o computador fará exatamente o que você manda. Não necessariamente o que você pretendia dizer. Os programas são perfeitos. Nós criamos os problemas.
Niet the Dark Absol
8
@Thevs Por que você recomendaria varmais letno contexto de esta resposta? Eu não entendo
Klaycon 28/01
4
@ Thevs Eu discordo totalmente de você. varpode ser propenso a erros se usado de forma descuidada. Verifique este violino
Cid
2
@Thevs, em que caso vartem alguma vantagem let? Deixe-me esclarecer: um contexto moderno quando ambos são opções e estou pedindo um código que se deve escrever. Quando perguntei isso antes, recebi respostas sobre "você pode declarar novamente uma variável com var"; nesse caso, tenho que lembrar às pessoas que você não deve declarar variáveis ​​novamente . Isso é um bug ou um erro na lógica do código - então o benefício da re-declaração é ... que permite que você escreva um código com defeito. Ainda não vi nenhuma razão sensata a favor de varquando lettambém é uma opção.
VLAZ 29/01
2
Marque outra marca contra javascript. Todas as variáveis ​​são globais, a menos que declaradas locais. :(
JRE
68

Na função first(), variáveis be csão criadas em tempo real, sem usar varou let.

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

É diferente de

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

Eles se tornam globais implícitos. É por isso que eles estão disponíveis emsecond()

Da documentação

A atribuição de um valor a uma variável não declarada o cria implicitamente como uma variável global (ela se torna uma propriedade do objeto global) quando a atribuição é executada.

Para evitar isso, você pode usar "use strict"isso fornecerá erros quando alguém usar uma variável não declarada

"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
fonte
15
Além disso: let a = 10, b = 10, c = 10;ou let a, b, c; a = b = c = 10;seria a maneira correta de declarar as variáveis.
Rickard Elimää 27/01
Então, com o modo estrito, e a variável b?
Tick20
2
@ Tick20 a variável bnão será avaliada / alcançada, ocorrerá um erro na linha let a = b = c = 10;, lido da direita para a esquerda . csendo a primeira variável causando ReferenceError, o restante da linha não será executado (o script parou)
Cid
2
algo como let a = 10, b = a, c = b;é válido também
Kaddath 28/01
8
votado principalmente para o "uso estrito". No contexto de uma pergunta de entrevista, isso também seria o começo do meu comentário sobre este código.
Pac0 28/01
23

Antes de chamar as coisas de estranhas, vamos conhecer alguns princípios básicos primeiro:

var e let são usados ​​para declaração de variáveis ​​em JavaScript. Por exemplo,

var one = 1;
let two = 2;

Variáveis ​​também podem ser declaradas sem usar varou let. Por exemplo,

three = 3;

Agora, a diferença entre as abordagens acima é a seguinte:

var é o escopo da função

e

let tem escopo definido no bloco.

enquanto o escopo das variáveis ​​declaradas sem var/ letpalavra-chave se torna global, independentemente de onde é declarado.

As variáveis ​​globais podem ser acessadas de qualquer lugar da página da web (não recomendado, porque as globais podem ser modificadas acidentalmente).

Agora, de acordo com esses conceitos, vamos dar uma olhada no código em questão:

 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
fonte
11
Você plagiou parte da resposta de JonoJames . Por quê?
Peter Mortensen
2
Sinto muito, mas eu não tinha essa intenção, algo semelhante pode estar lá, porque podemos ter coletado um pedaço de informação da mesma fonte.
fatimasajjad 29/01
2
Pode ser que ambas as respostas contenham conteúdo copiado da mesma fonte original sem citação aparente - possivelmente tutorialsteacher.com/javascript/javascript-variable . A presença de plágio é aparente, uma vez que um erro gramatical é reproduzido do original - "o escopo das variáveis ​​declaradas sem a varpalavra - chave se torna global, independentemente de onde ela é declarada" deveria ser "o escopo ... se torna" ou "o escopos ... tornam-se " . Usar as palavras exatas de outra pessoa requer citação, seja daqui ou de outro lugar. meta.stackexchange.com/q/160071/211183
Michael - sqlbot 30/01
Obrigado pessoal, eu adicionei um link de referência para a fonte.
fatimasajjad 31/01
6

Variáveis ​​que usam a letpalavra-chave devem estar disponíveis apenas dentro do escopo do bloco e não disponíveis em uma função externa ...

Cada variável que você está declarando dessa maneira não está usando letou var. Está faltando uma vírgula na declaração das variáveis.

Não é recomendável declarar uma variável sem a varpalavra - chave. Pode substituir acidentalmente uma variável global existente. O escopo das variáveis ​​declaradas sem a varpalavra - chave se torna global, independentemente de onde ela é declarada. Variáveis ​​globais podem ser acessadas de qualquer lugar da página da 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
fonte
3

É porque, quando você não usa letou varentão a variável está sendo declarada em tempo real, é melhor você declarar como segue.

let a = 10;
let b = 10;
let c = 10;
Mac Rathod
fonte
2

O problema estranho é causado por regras de escopo no 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
}

Supondo que você queira declarar 3 variáveis ​​locais inicializadas com o mesmo valor (100). O seu primeiro () será parecido abaixo. Nesse caso, second () não terá acesso a nenhuma das variáveis ​​porque elas são locais para 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
}

No entanto, se você deseja variáveis ​​globais , o seu primeiro () será semelhante a seguir. Nesse caso, o segundo terá acesso a todas as variáveis ​​porque elas estão no escopo global

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
}

Variáveis ​​locais (também conhecidas como acessíveis no bloco de códigos onde são declaradas).
Um bloco de código é qualquer {} com linha (s) de código entre.

  • function () {var, let, const aqui é acessível a toda a função},
  • para () {var aqui é acessível ao escopo externo, deixe const constante apenas aqui},
  • etc.

Variáveis ​​globais (também conhecidas como acessíveis no escopo global).
Essas variáveis ​​estão anexadas ao objeto global. O objeto global depende do ambiente. É o objeto de janela nos navegadores.

Nota especial: você pode declarar variáveis ​​no JavaScript sem usar as palavras-chave var, let, const. Uma variável declarada dessa maneira é anexada ao objeto global, portanto acessível no escopo global.
a = 100 // is valid and is in global scope

Alguns artigos para leitura adicional: https://www.sitepoint.com/demystifying-javascript-variable-scope-hoisting/ https://scotch.io/tutorials/understanding-scope-in-javascript https: //www.digitalocean .com / community / tutoriais / compreensão-variáveis-escopo-levantamento-em-javascript

Funwie
fonte
0

A principal diferença está nas regras de escopo. As variáveis ​​declaradas pela palavra-chave var têm o escopo definido para o corpo da função imediata (daí o escopo da função) enquanto as variáveis ​​permitidas têm o escopo definido para o bloco de fechamento imediato indicado por {} (portanto, o escopo do bloco). E quando você diz

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

c e b têm uma vida útil tão divertida, mas apenas têm uma extensão de bloco e, se você tentar acessar a referenciando, sempre mostra erro, mas c e b são globalmente, portanto não o fazem. Você perceberá que não importa quantas variáveis ​​adicionadas à sua cadeia, ela será apenas a primeira (a) que causa o erro. Isso ocorre porque "let" coloca sua variável no bloco (ou "localmente", mais ou menos significa "entre parênteses") no qual você a declara. Se você declarar uma variável sem "let", ela escopo a variável globalmente.Então, na função em que você define suas variáveis, tudo obtém o valor 10 (você pode ver isso no depurador se colocar um ponto de interrupção). Se você colocar um log do console para a, b, c nessa primeira função, tudo ficará bem. Mas, assim que você sair dessa função, a primeira (a) - e novamente, lembre-se de que,

Muhammad Fahad
fonte
0

Aqui estão os três aspectos interessantes das declarações de variáveis ​​no JavaScript:

  1. var restringe o escopo da variável ao bloco em que está definida. ( 'var' é para escopo local .)

  2. let permite a substituição temporária do valor de uma variável externa dentro de um bloco.

  3. Simplesmente declarar uma variável sem var ou let tornará a variável global, independentemente de onde for declarada.

Aqui está uma demonstração do let , que é a mais recente adição ao idioma:

// 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))

Resultado:

$ 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

Explicação:

As variáveis a e b foram declaradas dentro ' first () ', sem var ou deixar palavras-chave.

Portanto, a e b são globais e, portanto, são acessíveis durante todo o programa.

Na função denominada 'second' , a instrução 'let a = 5' define temporariamente o valor de ' a ' para ' 5 ', apenas no escopo da função.

Fora do escopo de ' second () ', ou seja, no escopo global, o valor de ' a ' será como definido anteriormente.

Gopinath
fonte