2017-02-21 3 views
4
var addCount; 
function s1() { 
    var count = 0; 
    addCount = function() { 
     count++; 
    }; 
    function s12() { 
     console.log(count) 
    } 
    return s12 
} 
var result1 = s1(); 
var result2 = s1(); 
addCount(); 
result1(); // count = 0; 
result2(); // count = 1; 

In the picture I marked the puzzled place Затем, на следующем этапе будет показано, таким образом This is where I am really puzzledПочему этот код имеет два разных результата?

+0

После каждого вызова s1, addCount ссылается на переменную count, на которую ссылается замыкание, созданное этим вызовом. Таким образом, addCount() при вызове ссылается на переменную count в замыкании, на которую ссылается result2 – antlersoft

+0

Это обсуждается в ответах, но я хочу обратить внимание на ваше внимание (Rodney_Dian): ответ, который вы приняли, неверен и будет путать ваше понимание дальше. Вы можете доказать, что это неправильно, добавив больше вызовов в 'result1()' и 'result2()' в конце вашего кода. Вы обнаружите, что независимо от того, сколько раз или в каком порядке вы их называете, каждый вызов 'result1()' возвращает 0, и каждый вызов 'result2()' возвращает 1. (Если вы не вызываете 'addCount()' снова , что приведет к дальнейшему увеличению 'result2()', оставив 'result1()' все еще на 0.) Прочтите другие ответы ниже, чтобы понять, почему. –

ответ

5

Потому что result - это функция, объявленная как результат вызова функции s1. Вызов s1 возвращает функцию s12, и эта функция использует переменную с именем count, которая объявляется на более высоком уровне (сфере), чем сама (это называется «свободной переменной»).

Когда свободная переменная используется внутри функции, которая имеет срок службы, которое больше, чем функции, где была объявлена ​​свободная переменная, A "closure" создается вокруг той свободной переменной, и он остается в области видимости, даже после того, как функции он был объявлен в завершение.

Когда вы вызываете result в первый раз, count увеличивается на единицу, и это значение остается в памяти, так что, когда вы его вызываете второй раз, вы работаете с последним значением.

+0

большое вам спасибо! –

+0

@Rodney_Dian Добро пожаловать. Не забывайте голосовать и отмечать как ответ. –

+0

Вызов 'result' (я думаю, вы имеете в виду' result1' или 'result2') не увеличивает' count'. –

1

Это потому, что переменная addCount имеет глобальный масштаб.

+3

Думаю, я бы не сказал, что это неправильно, но это, безусловно, делает некоторые нечетные предположения о том, где понимается разрыв в уровне –

0

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

Кроме этого, ваш метод addCount является глобальным для обоих вызовов. Второй вызов отменяет первый.

Итак, в новом методе addCount у вас есть область вашего второго звонка s1.

Таким образом, ваш звонок addCount имеет вторую переменную count и увеличивает ее.

При печати результатов вы возвращаетесь в обе области и получаете правильные значения count = 0 в рамках вашего первого вызова и count = 1 в рамках вашей второй.

+0

Вы не создаете две переменные с именем 'count'.Существует закрытие вокруг единственной переменной 'count', и оба вызова функции делятся ею. У вас не может быть двух одинаково названных объектов, объявленных в той же области. Если вы это сделаете, последний объявленный перепишет предыдущий. Как только значение изменится с 1 на 2, нет возврата и получения 1 снова. –

+0

@ScottMarcus - Вы ошибаетесь. Переменные ** не ** разделяются, как демонстрирует этот пример. Закрытие - все о возврате и получении 1 снова; это точно, что они делают. –

+0

@MarkAdelsberger Нет, вы ошибаетесь. Закрытие - это общий объем свободных переменных. Исправление непредвиденных последствий закрытия - это создание отдельных областей путем создания копии переменной или создания выделенных областей (например, с 'let'). –

5

Чтобы понять это поведение, вам необходимо понять, что такое закрытие. Короткий (но, вероятно, запутанным, если вы на самом деле не в ладу с закрытием) ответ:

Каждый вызов s1 выполняет следующие действия:

A) Создайте переменную с именем счетчика и инициализировать его в 0

Это кажется достаточно простым ...

B) Создайте функцию, которая увеличивает переменную из (а), и установить переменную addCount, чтобы указать на него

Некоторые важные детали здесь скрыты. Переменная, которую эта функция будет обновлять, равна той, которая была создана на этапе (A) , тем же вызовом из s1. Это следствие механизма закрытия.

Конечно, когда второй вызов s1 достигает этот шаг, он переписывает значение addEvent, заменив функцию, который увеличивает на count созданного предыдущего вызова s1 с новой функцией, которая увеличивает значение count, созданные этот текущим вызов от s1.

С) Создает и возвращает функцию, которая записывает значение переменной, созданной в (А)

Как и (B), то эта функция тоже видит переменную, созданную стадии А, текущего вызова от s1.

Так что это значит?

Ну, вы называете s1 один раз, и это создает переменную со значением 0, и возвращает функцию, которая регистрирует эту переменную (функции, которую припасли в result1).

В качестве побочного эффекта этот вызов установил addCount функции, которая изменила бы значение переменной, зарегистрированной на result1, если вы ее вызвали ... но вы ее не называете.

Вместо этого вы вызываете s1, который заменяет addCount новой функцией, которая обновляет новую переменную (также инициализируется до 0). Он возвращает функцию, которая регистрирует эту новую переменную, которую вы храните как result2.

Теперь вы делаете вызов через addCount, который вызывает функцию второго обновляет вторую переменной . Затем вы вызываете result1, регистрируя первую переменную (которая никогда не обновлялась), а затем вы вызываете result2, регистрируя вторую переменную (которую вы увеличили один раз).

Смежные вопросы