Я понимаю, что объявления функций поднимаются в верхней части их области. Это позволяет использовать эти функции до того, как они будут объявлены в JavaScript:
sayHello(); // It works!
function sayHello() {
console.log('Hello');
}
Я также понимаю, что замыкания позволяют функциям сохранять ссылки на переменные, объявленные в той же области:
function outerFxn() {
let num = 0;
function innerFxn() {
console.log(num);
num++;
}
return innerFxn;
}
const logNum = outerFxn();
logNum(); // 0
logNum(); // 1
logNum(); // 2
Все идет нормально. Но здесь какая-то странность, что я надеюсь, что кто-то сможет точно объяснить, что происходит...
function zero(cb) {
return setTimeout(cb, 0);
}
function test1() {
let txt = 'this is a test message';
function log() {
console.log(txt);
}
zero(log);
}
В приведенном выше примере функция log
сохраняет ссылку на область, в которой она была создана, удерживая переменную txt
. Затем, когда он выполняется позже в setTimeout
, он успешно регистрирует значение переменной txt
. Отлично. Тогда вот это...
function zero(cb) {
return setTimeout(cb, 0);
}
function test1() {
function log() {
console.log(txt);
}
let txt = 'this is a test message';
zero(log);
}
Я переместил объявление функции log
в верхнюю часть области (было бы все равно вставлено, верно?), Тогда я объявляю переменную txt
ниже нее. Это все еще работает, и я не уверен, почему. Как log
сохраняет ссылку на переменную txt
если let
и const
не подняты? Рассматриваются ли области охвата как целое? Я мог бы немного понять, что движок JavaScript делает шаг за шагом здесь. Спасибо, земля!
"Анализируются ли закрытые области как целое?" - да. Закрытие сохраняет область действия, как и в тот момент, когда вы (лексически) покидаете ее. В вашем примере txt
существует, когда вы достигаете закрытия }
в test1
, поэтому он находится в области видимости, и log
не имеет проблем с доступом к нему.
Примечание "lexically" выше: привязки выполняются до времени выполнения, когда важна только ваша вещь. Таким образом, даже это сработает, хотя это не должно быть "с динамической" точки зрения:
function test1() {
function log() {
console.log(txt);
}
zero(log);
let txt = 'this is a test message';
}
Это часть области действия после того, как вы оставите функцию test1
. Не имеет значения, если он используется с var
, let
или const
в этой точке. Поскольку весь объект был оценен, переменная существует в области.
Если бы вы пытались использовать log
до того, как let
объявление let
, вы получили ошибку.
Редактирование: технически переменные, объявленные с let
и const
находятся в области видимости, но они являются униализированными, что приводит к ошибке, если вы пытаетесь получить к ним доступ. Это только до тех пор, пока вы не дойдете до деклараций, которые они инициализированы, и вы можете получить к ним доступ. Поэтому они всегда находятся в сфере охвата, просто недоступны до тех пор, пока не будут оценены декларации.
var
, которые подняты (существуют, но не инициализированы), let
физически не существуют до объявления.
это указание времени/исполнения. Подумайте об этом
function test1(){
var context = { };
function log(){
if(context.hasOwnProperty("txt")){
console.log(context.txt);
}else{
throw new Error("there is no value 'txt' declared in this context");
}
}
context.txt = 'this is a test message';
log();
}
То же самое в вашем коде с нестойкой переменной txt
. В то время log
выполнен, let txt
будет объявлен в правильном контексте функции. Он доступен, даже если он не поднят. Функция log
не хранит ссылку на саму переменную, но в целом окружающий контекст выполнения, и этот контекст хранит переменные.
В Сценарии 2 вы делаете:
let txt = 'this is a test message'
что означает, что txt
будет частью области test1()
.log()
который будет иметь доступ к области своего родительского test1()
. Итак, что происходит во время выполнения? test1()
будет оцениваться, и как таковой log()
будет иметь доступ к области test1()
. Это означает, что txt
будет доступен для использования log()
для немедленного использования.
Совет: отлаживайте его, ставьте несколько точек останова и смотрите, что происходит.
Изменить: вы также можете считать, что в log()
txt
не определен, и поэтому его значение должно быть неопределенным... правильно? Тот факт, что console.log(txt)
работает, выводит this is a test message
из-за приведенного выше пояснения по области видимости. Всегда хорошая практика объявлять ваши переменные в верхней части области функций, а ваши функции внизу области действия, поскольку они будут оцениваться в первую очередь. Рассмотрите человеческий фактор в этой ситуации, лучшая практика также может означать: для вас/любого, кто понимает, что делает код, просто читая его.