Как работает это закрытие JavaScript? - PullRequest
4 голосов
/ 10 июня 2010

Вот немного JavaScript:

linkElem.click(function () {
    var data = linkElem.data();
    alert(''+data.mls + ' ' + data.id);
});

Работает.

linkElem - это локальная переменная, которую я создаю в цикле внутри функции. Я назначаю ему некоторые данные с помощью jQuery .data(). Если бы я не вызывал .click(), linkElem был бы переназначен во время цикла и затем возвращен в исходное состояние после возврата функции. Тем не менее, я создал анонимную функцию, которая ссылается на linkElem. Поэтому я больше не уверен, что происходит.

Я предполагаю, что всем анонимным функциям и linkElem, созданным во время цикла, присваиваются какие-то идентификаторы UID, и они перемещаются в постоянную / глобальную область. Это правильно? Буду благодарен за детали.

Ответы [ 3 ]

4 голосов
/ 10 июня 2010

Да, ваше описание довольно близко. Локальное хранилище для вызова функции Javascript - это просто блок памяти, выделенный для локальных переменных. Если вы «захватите» это, создав другую функцию внутри вызываемой функции, то хранилище будет сохранено, а локальные переменные продолжат свою жизнь, не зная, что функция, которая их родила может быть давно мертв.

Важно помнить, что функции только создают такое хранилище & mdash; такие вещи, как заключенные в скобки тела петель, являются , а не отдельными областями хранения. Таким образом, распространенной ошибкой является объявление переменной в функции и ее повторное использование среди нескольких функций, созданных в цикле. Это не по своей сути неправильно, но эффект может быть удивительным:

function whatever() {
  for (var i = 0; i < 3; ++i) {
    setTimeout(function() { alert(i); }, 5000);
  }
}

Если вы запустите это, вы увидите три предупреждения, которые все говорят "3". Зачем? Потому что все они имеют одну и ту же переменную "i". Вы можете избежать этого, введя другой функциональный слой:

function whatever() {
  for (var i = 0; i < 3; ++i) {
    setTimeout((function(private_i) { return function() { alert(private_i); }; })(i), 5000);
  }
}

Функция "обертка" предназначена для предоставления локальной переменной (параметр "private_i"), в которую можно скопировать переменную цикла "i".

1 голос
/ 06 января 2014

Пожалуйста, обратитесь к этому Как работают закрытия JavaScript? Это может помочь вам понять замыкания.

Всякий раз, когда вы видите ключевое слово функции в другой функции, внутренняя функция имеет доступ к переменным во внешней функции.

function foo(x) {
  var tmp = 3;
  function bar(y) {
    alert(x + y + (++tmp));
  }
  bar(10);
}
foo(2)

Это всегда выдаст 16, потому что bar может получить доступ к x, который был определен как аргумент для foo, и также может получить доступ к tmp из foo.

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

function foo(x) {
  var tmp = 3;
  return function (y) {
    alert(x + y + (++tmp));
  }
}
var bar = foo(2); // bar is now a closure.
bar(10);

Вышеприведенная функция также выдаст предупреждение 16, поскольку bar может по-прежнему ссылаться на x и tmp, даже если она больше не находится непосредственно внутри области действия.

Однако, поскольку tmp все еще находится внутри закрытия bar, оно также увеличивается. Он будет увеличиваться каждый раз, когда вы звоните bar.

Простейший пример закрытия - это:

var a = 10;
function test() {
  console.log(a); // will output 10
  console.log(b); // will output 6
}
var b = 6;
test();

Когда вызывается функция Javascript, создается новый контекст выполнения. Вместе с аргументами функции и родительским объектом этот контекст выполнения также получает все переменные, объявленные вне его (в вышеприведенном примере оба 'a' и 'b').

Можно создать более одной функции замыкания, либо возвращая их список, либо задавая им глобальные переменные. Все они будут относиться к одинаковым x и тем же tmp, они не делают свои собственные копии.

[you]: Увлекательно, расскажи мне больше!

Здесь число x является буквальным числом. Как и в случае с другими литералами в JavaScript, при вызове foo число x равно , скопировано в foo в качестве аргумента x.

С другой стороны, JavaScript всегда использует ссылки при работе с объектами. Если, скажем, вы вызвали foo с объектом, возвращаемое им закрытие будет ссылаться на этот оригинальный объект!

function foo(x) {
  var tmp = 3;
  return function (y) {
    alert(x + y + tmp);
    x.memb = x.memb ? x.memb + 1 : 1;
    alert(x.memb);
  }
}
var age = new Number(2);
var bar = foo(age); // bar is now a closure referencing age.
bar(10);

Как и ожидалось, каждый вызов bar(10) будет увеличиваться x.memb. Чего нельзя ожидать, так это того, что x просто ссылается на тот же объект, что и переменная age! После пары звонков на bar, age.memb будет 2! Эта ссылка является основой для утечек памяти с объектами HTML.

1 голос
/ 10 июня 2010

Однако я создал анонимную функцию, которая ссылается на linkElem. Поэтому я больше не уверен, что происходит.

Он все еще переназначается, если вы не заключаете его в другой уровень области видимости (NB: другая функция).

Обратите внимание на следующее:

for (var j = 0;j < 10;j += 1) {
    arrayOfLinks[j].onclick = function () {
        alert(j);
    };
}

В этом случае все эти ссылки будут оповещать 10 при щелчке, потому что j находится вне области и обновляется.

Если вы создаете linkElem таким же образом, вы, скорее всего, получите только результат last linkElem в цикле.

Это лучший способ:

linkElem.click(function () {
    var data = $(this).data(); // no longer dependent on `linkElem` reference
    alert(''+data.mls + ' ' + data.id);
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...