Почему некоторые переменные, объявленные с использованием параметра let inside, становятся доступными в другой функции, в то время как другие приводят к ошибке ссылки?


158

Я не могу понять, почему переменные действуют так странно, когда объявлены внутри функции.

  1. В firstфункции, которую я объявляю с letпеременными bи cсо значением 10 :

    b = c = 10;

    В secondфункции я показываю:

    b + ", " + c

    И это показывает:

    10, 10
  2. Также в firstфункции я объявляю aсо значением 10 :

    let a = b = c = 10;

    Но в secondфункции это показывает ошибку:

    Не могу найти переменную: a

  3. Теперь в firstфункции я объявляю dсо значением 20 :

    var d = 20;

    Но в secondфункции она показывает ту же ошибку, что и раньше, но с переменной d:

    Не могу найти переменную: d

Пример:

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


31
Вы объявляя глобалам, так bи cне предваряется varключевым словом. aи dявляются местными для first.
ВЛАЗ

1
Комментарии не для расширенного обсуждения; в чате был заархивирован разговор о том, будет ли это хороший вопрос для интервью .
Коди Грей

1
Это напоминает мне о похожей ситуации в Visual Basic; Dim Apple, Banana, Pear As Fruitзначит Dim Apple / Dim Banana / Dim Pear As Fruit, а нет Dim Apple As Fruit / ....
Эрик Липперт

Ответы:


179

Это потому, что вы на самом деле говорите:

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

И не то, что вы думаете, что говорите, а именно:

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

Вы заметите, что независимо от того, сколько переменных вы добавляете в свою цепочку, только первая (а) вызывает ошибку.

Это потому, что «let» определяет область видимости вашей переменной в блоке (или «локально», более или менее означающем «в скобках»), в котором вы ее объявляете.

Если вы объявляете переменную без «let», она охватывает область глобально.

Итак, в функции, в которой вы устанавливаете свои переменные, все получает значение 10 (вы можете увидеть это в отладчике, если вы установите точку останова). Если вы поместите консольный журнал для a, b, c в эту первую функцию, все будет хорошо.

Но как только вы покидаете эту функцию, первая (а) - и опять же, имейте в виду, технически в порядке назначения, она последняя - «исчезает» (опять же, вы можете увидеть это в отладчик, если вы установили точку останова во второй функции), но другие два (или сколько вы добавляете) по-прежнему доступны.

Это связано с тем, что «let» ТОЛЬКО ПРИМЕНЯЕТСЯ (так что только локально ограничивает) ПЕРВОЙ ПЕРЕМЕННОЙ - опять же, которая технически является последней, которая будет объявлена ​​и ей присвоено значение - в цепочке. У остальных технически нет «пускай» перед ними. Так что они технически объявлены глобально (то есть в глобальном объекте), поэтому они появляются в вашей второй функции.

Попробуйте: удалите ключевое слово let. Все ваши варианты теперь будут доступны.

«var» имеет аналогичный эффект локальной области видимости, но отличается тем, как переменная «поднимается», что вам определенно нужно понять, но которое напрямую не связано с вашим вопросом.

(Кстати, этот вопрос достаточно озадачил бы разработчиков JS, чтобы сделать его хорошим).

Настоятельно советуем вам потратить время на различия в том, как переменные могут быть объявлены в JS: без ключевого слова, с «let» и с «var».


4
Это одновременно лучшая и худшая вещь в программировании: компьютер будет делать именно то , что вам говорят. Не обязательно то, что вы намеревались сказать. Программы идеальны. Мы создаем проблемы.
Niet the Dark Absol

8
@Thevs Почему вы рекомендуете varболее letв контексте этого ответа? Я не понимаю
Клайкон

4
@Thevs Я категорически не согласен с вами. varможет быть подвержен ошибкам, если используется небрежно. Проверьте эту скрипку
Cid

2
@Thevs в каком случае varимеет какое-либо преимущество перед let? Позвольте мне уточнить: современный контекст, когда оба варианта являются опциями, и я прошу код, который нужно написать. Когда я спрашивал об этом раньше, я получал ответы о «вы можете повторно объявить переменную с помощью var», и в этом случае я должен напомнить людям, что вы не должны повторно объявлять переменные . Это либо ошибка, либо ошибка в логике кода - так что преимущество повторного объявления заключается в том ... что он позволяет писать ошибочный код. Я еще не видел какой-либо разумной причины в пользу того, varкогда letэто тоже вариант.
ВЛАЗ

2
Выделите еще один знак против JavaScript. Все переменные являются глобальными, если не объявлены локальными. :(
JRE

68

В функции first()переменные bи cсоздаются на лету, без использования varили let.

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

Отличается от

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

Они становятся неявными глобальными. Вот почему они доступны вsecond()

Из документации

Присвоение значения необъявленной переменной неявно создает его как глобальную переменную (она становится свойством глобального объекта) при выполнении присваивания.

Чтобы избежать этого, вы можете использовать метод, "use strict"который будет выдавать ошибки при использовании необъявленной переменной

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


15
Кроме того: let a = 10, b = 10, c = 10;или let a, b, c; a = b = c = 10;иначе был бы правильный способ объявления переменных.
Рикард Элимяэ

Так что в строгом режиме, как насчет переменной b?
Tick20

2
@ Tick20 переменная bне будет оценена / достигнута, ошибка произойдет в строке let a = b = c = 10;, прочитанной справа налево . cБудучи первой вызывающей переменной ReferenceError, оставшаяся часть строки не будет выполнена (сценарий остановлен)
Cid

2
что-то подобное let a = 10, b = a, c = b;тоже действительно
Каддат

8
проголосовал в основном за «строгое использование». В контексте вопроса об интервью это также послужило началом моего комментария к этому коду.
Pac0

23

Прежде чем называть вещи странными, давайте сначала узнаем некоторые основы:

var и let оба используются для объявления переменных в JavaScript. Например,

var one = 1;
let two = 2;

Переменные также могут быть объявлены без использования varили let. Например,

three = 3;

Теперь разница между вышеуказанными подходами заключается в том, что:

var является функцией области

а также

let находится в блоке

в то время как область видимости переменных, объявленных без ключевого слова var/, letстановится глобальной независимо от того, где она объявлена.

Доступ к глобальным переменным можно получить из любой точки веб-страницы (не рекомендуется, поскольку глобальные переменные могут быть случайно изменены).

Теперь в соответствии с этими концепциями давайте посмотрим на рассматриваемый код:

 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
}

1
Ты плагиат часть ответа JonoJames . Почему?
Питер Мортенсен

2
Извините, но у меня не было такого намерения, может быть что-то подобное, потому что мы могли собрать часть информации из того же источника.
Фатимасаджад

2
Возможно, оба ответа содержат контент, скопированный из одного и того же исходного источника без явной ссылки - возможно, tutorialsteacher.com/javascript/javascript-variable . Присутствие плагиата очевидно, поскольку грамматическая ошибка воспроизводится из оригинала: «область действия переменных, объявленных без varключевого слова, становится глобальной независимо от того, где оно объявлено», должно быть либо «область действия… становится», либо « прицелы ... становятся " . Использование точных чужих слов требует цитирования, будь то здесь или где-то еще. meta.stackexchange.com/q/160071/211183
Майкл - sqlbot

Спасибо, ребята, я добавил ссылку на источник.
Фатимасаджад

6

Переменные, использующие letключевое слово, должны быть доступны только в пределах блока и не доступны во внешней функции ...

Каждая переменная, которую вы объявляете таким образом, не использует let или var. Вам не хватает запятой в объявлении переменных.

Это не рекомендуется , чтобы объявить переменную без varключевого слова. Он может случайно перезаписать существующую глобальную переменную. Область действия переменных, объявленных без varключевого слова, становится глобальной независимо от того, где оно объявлено. Глобальные переменные могут быть доступны из любой точки веб-страницы.

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


3

Это из-за того, что когда вы не используете letили varтогда переменная становится объявленной на лету, лучше объявить следующим образом.

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

2

Странная проблема вызвана ограничением правил в 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
}

Предполагая, что вы хотите объявить 3 локальные переменные, инициализированные одним и тем же значением (100). Ваша первая () будет выглядеть ниже. В этом случае second () не будет иметь доступа ни к одной из переменных, потому что они локальны для 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
}

Однако, если вам нужны глобальные переменные, ваш first () будет выглядеть следующим образом. В этом случае секунда будет иметь доступ ко всем переменным, потому что они находятся в глобальной области видимости.

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
}

Локальные переменные (ака. Доступны в блоке кода, где они объявлены).
Блок кода - это любой {} со строкой кода между.

  • function () {var, let, const здесь доступен для всей функции},
  • for () {переменная здесь доступна для внешней области, пусть const доступна только здесь},
  • и т.п.

Глобальные переменные (они также доступны в глобальной области видимости).
Эти переменные привязаны к глобальному объекту. Глобальный объект зависит от окружающей среды. Это оконный объект в браузерах.

Специальное примечание: Вы можете объявлять переменные в JavaScript без использования ключевых слов var, let, const. Объявленная таким образом переменная присоединяется к глобальному объекту, поэтому доступна в глобальной области видимости.
a = 100 // is valid and is in global scope

Некоторые статьи для дальнейшего чтения: https://www.sitepoint.com/demystifying-javascript-variable-scope-hoisting/ https://scotch.io/tutorials/understanding-scope-in-javascript https: //www.digitalocean .com / сообщества / учебники / понимание-переменные-Scope подъемно-в-JavaScript


0

Основное отличие заключается в правилах определения объема. Переменные, объявленные ключевым словом var, попадают в область непосредственного тела функции (отсюда область действия функции), а переменные let - в область непосредственного включения, обозначенную {} (отсюда область действия блока). И когда вы говорите

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

c и b имеют такой же жизненный цикл, как и забавный, но имеют только блок с продолжительностью блока, и если вы пытаетесь получить доступ к a, ссылаясь на него, всегда показывает ошибку, но c и b глобально, поэтому их нет. Вы заметите, что независимо от того, сколько Переменные, которые вы добавляете в свою цепочку, это будет только первая (а), которая вызывает ошибку. Это потому, что «let» определяет вашу переменную в блоке (или, «локально», более или менее означает «в скобках») в котором вы объявляете его. Если вы объявляете переменную без «let», она определяет глобально переменную. Так, в функции, в которой вы устанавливаете переменные, все получает значение 10 (вы можете увидеть это в отладчике, если вы поставите Распад точка). Если вы поместите консольный журнал для a, b, c в эту первую функцию, все будет хорошо. Но как только вы выйдете из этой функции, первая (a) - и снова, имейте в виду,


0

Вот три интересных аспекта объявления переменных в JavaScript:

  1. var ограничивает область видимости переменной блоком, в котором она определена. ( 'var' для локальной области .)

  2. let допускает временное переопределение значения внешней переменной внутри блока.

  3. Простое объявление переменной без var или let сделает переменную глобальной, независимо от того, где она объявлена.

Вот демонстрационная версия let , которая является последним дополнением к языку:

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

Вывод:

$ 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

Объяснение:

Переменные a и b были разделены внутри first () , без ключевых слов var или let.

Следовательно, a и b являются глобальными и, следовательно, доступны по всей программе.

В функции с именем 'second' оператор 'let a = 5' временно устанавливает значение ' a ' в ' 5 ', только в рамках функции.

Вне области применения ' second () ', IE, в глобальной области видимости значение ' a ' будет таким, как определено ранее.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.