var foo = (function(){
var x = 0;
return function(){return x++;};
})()
Почему выражение var x = 0 работает только один раз, это самое большое недоразумение по этому поводу.
var foo = (function(){
var x = 0;
return function(){return x++;};
})()
Почему выражение var x = 0 работает только один раз, это самое большое недоразумение по этому поводу.
Ваш код:
var foo = (function(){
var x = 0;
return function(){return x++;};
})()
эквивалентен этому коду:
function f(){
var x = 0;
return function(){return x++;};
}
var foo = f();
Легко видеть, когда вы разбиваете это так, что функция f()
вызывается только один раз. Он определяет x
, а затем возвращает новую функцию, которая определена внутри локальной области f
. Эта новая функция часто называется "анонимной функцией" (что означает, что она не имеет имени) или "закрытие". По правде говоря, все функции в javascript являются "замыканиями" - независимо от того, названы они или нет. Термин "закрытие" просто означает, что функция сохраняет доступ к переменным, которые были определены в области родительской функции - даже после выхода родительской функции.
Итак, теперь foo
содержит новую функцию (закрытие), которая была возвращена из f
. Вы можете вызывать foo()
столько раз, сколько захотите - и каждый раз, когда вы это делаете, x
будет возвращаться и после этого увеличивается. Поскольку x
существует в родительской области замыкания, его значение будет сохраняться на нескольких вызовах закрытия.
Что еще... ни один другой код не имеет доступа к x
после выхода из f()
- это в основном означает, что x
теперь является "личными данными" закрытия. Довольно аккуратный ха?
Переменная foo
присваивается результату функции самоисполнения, которая выглядит следующим образом:
Объявляет переменную с именем x
, инициализированную на 0
. Возвращает функцию, которая при вызове увеличивает значение x
.
Итак, в этот момент foo
ссылается на функцию.
Способ вызова:
foo();
При первом вызове возвращаемое значение будет 0
, затем 1
, 2
...
Ну, подождать минуту..., не должно быть 1
, 2
, 3
...?
Вы на правильном пути, но причина в том, что в этом случае это неверно, объясняется различием между предварительным приращением и post-increment. (++var
vs var++
). Разница заключается в том, что результатом предварительного приращения является значение переменной после приращения, в то время как результат post-increment является значением переменной до приращения.
Этот пример иллюстрирует концепцию закрытия, что по сути означает, что внутренние функции имеют доступ к переменным, определенным в их окружающие функции.
Позвольте сломать его... Сначала мы определяем анонимную функцию:
(function() { ... })
Затем мы немедленно его выполним:
(function() { ... })()
Результатом этого выполнения является еще одна функция:
function(){return x++;}
И x = 0 захватывается замыканием, когда мы создали указанную выше функцию. Затем мы присваиваем эту результирующую функцию foo:
var foo = function(){return x++;}
Со значением x, захваченным закрытием. Всякий раз, когда выполняется foo, x увеличивается.
Анонимная функция вызывается немедленно следующим ()
(без параметров). Эта функция при выполнении возвращает другую функцию, у которой есть собственная переменная x
, которая увеличивается при запуске.
Итак, foo()
будет 0
первым запуском, 1
вторым и т.д., так как x
он был создан с продолжением увеличения.
Фактически в этом случае x
является локальной переменной в блоке/закрытии, назначенной объекту foo
. Тот, который получает приращение при вызове foo()
.
Проверьте это в действии - http://jsfiddle.net/3X283/
Это то, что называется closure theere - две функции, определенные - внутренняя (которая является закрытием) и внешняя который создает и возвращает замыкание.
Ваш код немедленно вызывает внешнюю функцию и присваивает результат (закрытие) foo
.
Обратите внимание, что код внутри замыкания не включает оператор var x = 0;
, поэтому при вызове foo()
он выполняет только код внутри замыкания (return x++;
)
x
, на который ссылается здесь, - это экземпляр, указанный в вызове. То, что делает замыкания интересными, состоит в том, что это x отличается от вызовов внешней функции - рассмотрим приведенный ниже пример. foo и bar будут увеличиваться независимо друг от друга, потому что они ссылаются на разные x
s
function makeClosure(){
var x = 0;
return function(){return x++;};
}
var foo = makeClosure();
var bar = makeClosure();
foo(); //returns 0
foo(); //returns 1
bar(); //returns 0
foo(); //returns 2