Вопрос о закрытии Javascript - PullRequest
2 голосов
/ 31 марта 2011

Я работаю с JQuery и jsTree, и у меня возникла путаница в отношении того, как работают замыкания.

У меня есть объект, который имеет член .jsTree и метод .populateTree. Метод вызывается с массивом строк, с помощью которого предполагается создать узлы jsTree.

jsTree создает древовидный элемент управления, в котором каждый узел имеет привязку "", которая содержит текст узла. Я хочу сделать нажатие на текст, чтобы переключить узел, открытый или закрытый, точно так же, как нажатие на кнопку +/- в дереве. Поэтому я пытаюсь добавить функцию click () для этого, и у меня неожиданное поведение.

Итак, вот код:

populateTree: function populateTree(nodeNames)
{
    if (!this.jsTree) // Note 1
        return;

    var me = this; // Note 2

    for (var i = 0; i < nodeNames.length; i++)
    {
        var nodeName = nodeNames[i];

        var node = this.jsTree.create_node(-1, "last", { state: 'open', data: nodeName }); //Note 3

        this.jsTree.create_node(node, "last", { data: "child one" }); // Note 4
        this.jsTree.create_node(node, "last", { data: "child two" });
        this.jsTree.create_node(node, "last", { data: "child three" });

        var anchor = node.find("a"); // Note 5
        anchor.click(function() { me.jsTree.toggle_node(node); }); // Note 6
    }
},
  • Примечание 1 : Это функция-член объекта javascript, поэтому при ее вызове this указывает на объект. Объект содержит переменную-член jsTree, которая должна быть уже инициализирована, чтобы содержать объект jsTree без узлов.

  • Примечание 2 : мы определяем функцию «щелчка» в Примечании 6, и когда она вызывается, «this» не будет указывать на объект, содержащий jsTree, поэтому мы сохраняем «this» в переменной с именем «me», которая будет находиться в области видимости при выполнении функции «click», потому что создание функции создавало замыкание, которое включало ссылки на все переменные, которые находились в области действия в момент определения функции.

  • Примечание 3 : Для каждого элемента в массиве мы создаем узел верхнего уровня (родительский узел равен -1).

  • Примечание 4 : Для каждого созданного нами узла верхнего уровня мы создаем три дочерних узла.

  • Примечание 5 : каждый узел содержит элемент привязки (""), и именно к этому мы хотим прикрепить функцию "щелчка".

  • Примечание 6 : внутри функции "click" "me" должно указывать на объект, содержащий дерево (см. Примечание 2), а "node" должно указывать на узел, который мы только что создали в текущем проходе через цикл (см. Примечание 3).

Моя проблема? Независимо от того, на какой якорь я нажимаю, это всегда последний узел верхнего уровня, который открывается и закрывается. Это похоже на замыкание для каждой из созданных нами функций «щелчка», которое имеет замыкание, которое ссылается только на последнюю переменную «узла». И я не думаю, что закрытие работает.

Может ли кто-нибудь помочь мне понять, где я ошибся в моем понимании?

Спасибо.

Ответы [ 3 ]

3 голосов
/ 31 марта 2011

Анонимная функция, которую вы прикрепляете в качестве обработчика кликов, закрывается над единичным node экземпляром, как только завершаются циклы, и много секунд спустя, когда пользователь нажимает на дерево, что анонимная функция выполняется, он будет смотреть на область, которую закрыл при создании, и увидит, что значение node - это то, что он в последний раз удерживал, как вы заметили, это значение последней итерации цикла.

Быстрое исправление может быть следующим:

anchor.click((function(node){ return function() { me.jsTree.toggle_node(node); }; })(node));

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

2 голосов
/ 31 марта 2011

Проблема в том, что в Javascript блоки не определяют область действия, а функции определяют.

Так что, хотя nodeName и node определены внутри цикла for, они действуют так же, как если бы онибыли определены снаружи, потому что блок цикла for не создает новую область видимости.

Вот почему в книге «Javascript: The Good Parts» Кроуфорд рекомендует, чтобы в Javascript вы определяли локальные переменные в началефункция, а не ближе всего к его использованию, как и на других языках.Определяя их далее внизу, выглядело бы так, как если бы оно было ограничено внутри содержащего блока, хотя в действительности это не так.

1 голос
/ 31 марта 2011

Помните, что замыкания являются статическими. Значение узла, используемого в вашем обработчике кликов, будет последним назначенным значением, а НЕ значением, которое было при создании обработчика кликов.

Исправление заключается в создании нового замыкания для каждого обработчика кликов:

var anchor = node.find("a"); // Note 5
(function (node) {
  anchor.click(function() { me.jsTree.toggle_node(node); }); // Note 6
}) (node);

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...