Почему переменная arsum, объявленная через let и ниже определения sum_fu(val), видна внутри sum_fu, как если бы я объявил ее через var?

Подскажите, почему я использую внутри функции sum_fu переменную arsum, но она объявлена позже, чем определение функции sum_fu и вдобавок arsum я объявляю через let, значит поднятия наверх нет (как если бы было через var) , тем не менее почему то это работает. Не понимаю, почему.

function sum_fu(val) {
arsum = arsum + val;
}

let arsum = 0;

let arr1 = [1, 2, 3];

arr1.forEach( sum_fu );

console.log(arsum);

Ответы (1 шт):

Автор решения: Stanislav Volodarskiy

Когда вы вызываете arr1.forEach( sum_fu );, то оба определения уже обработаны: sum_fu и arsum. Чтобы получить ошибку перенесите вызов arr1.forEach( sum_fu ); до определения arsum:

function sum_fu(val) {
arsum = arsum + val;
}

let arr1 = [1, 2, 3];
arr1.forEach( sum_fu );

let arsum = 0;

Ошибка формулируется как

"message": "ReferenceError: can't access lexical declaration 'arsum' before initialization"

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

Поднятие вверх здесь не при чём. Функция sum_fu компилируется с обращением к глобальному символу arsum. Наличие самого символа во время компиляции не нужно. Во время исполнения sum_fu символ отыскиваются в объемлющей области видимости, а в этот момент он уже определён (это про ваш оригинальный код).

Чтобы ощутить "поднятие" переменной к ней нужно обратится из кода в той же области видимости. Обратите внимание что "поднимается" только сама декларация (имя становится видимо), присвоение значения остаётся на месте:

console.log(a);
var a = 'A';
console.log(a);

Тот же код с let даёт ошибку:

console.log(a);
let a = 'A';
console.log(a);

→ Ссылка