jQuery, Javascript, нотация объекта - объяснение различий в поведении обратного вызова - PullRequest
1 голос
/ 17 августа 2011

Рассмотрим следующую реализацию jQuery, определенную с использованием литерала объекта ...

$(function() {

    var myObject = {

        methodOne: function()
        {

            $('#element').animate(
                {'marginLeft': '50px'},
                'slow',
                function() {
                    myObject.methodTwo();
                }
            );

        },

        methodTwo: function()
        {

            $('#element').animate(
                {'marginLeft': '-50px'},
                'slow',
                function() {
                    myObject.methodOne();
                }
            );

        }

    } // End myObject

    myObject.methodOne(); // Execute

});

Для записи, приведенный выше код работает так же, как и ожидалось. Чего я не понимаю, так это тонкого и, казалось бы, безобидного изменения, подобного следующему ...

    methodOne: function()
    {

        $('#element').animate(
            {'marginLeft': '50px'},
            'slow',
            myObject.methodTwo() // No more anonymous function
        );

    },

... для methodOne и methodTwo вызывает ошибку браузера с указанием too much recursion. Какая разница между тем, как я объявил мой обратный вызов? Кроме того, если я возвращаю объявление анонимной функции, но изменяю ссылку на объект, чтобы она выглядела следующим образом ...

    methodOne: function()
    {

        $('#element').animate(
            {'marginLeft': '50px'},
            'slow',
            function() {
                this.methodTwo(); // assuming 'this' refers to 'myObject'
            }
        );

    },

... Я получаю один хороший проход через methodOne, и после обратного вызова мой браузер выходит из себя, потому что не может найти methodTwo. Я предполагаю, что я где-то выпал из области видимости, но я не могу правильно решить, где. Ваше понимание высоко ценится!

Ответы [ 5 ]

2 голосов
/ 17 августа 2011

Давайте разберемся с этим

        $('#element').animate(
            {'marginLeft': '50px'},
            'slow',
            function() {
                myObject.methodTwo();
            }
        );

В этом примере вы передаете объект функции в качестве обратного вызова. Этот функциональный объект вызывается, когда анимация завершена. У него есть объект myObject, общий доступ к которому осуществляется через замыкание, поэтому он может легко найти его и вызвать метод для него. Awesome!

    $('#element').animate(
        {'marginLeft': '50px'},
        'slow',
        myObject.methodTwo() // No more anonymous function
    );

Здесь происходит нечто иное. В качестве обратного вызова здесь вы фактически передаете возвращаемое значение myObject.methodTwo(), а не фактический функциональный объект. Таким образом, поскольку methodTwo() ничего не возвращает, тогда undefined фактически передается как обратный вызов. Это означает, что функция animate() считает, что обратного вызова нет.

Так что, возможно, вы хотели попробовать это!

    $('#element').animate(
        {'marginLeft': '50px'},
        'slow',
        myObject.methodTwo // No more anonymous function or invocation
    );

Ну, это все равно не сработает. Теперь вы передаете объект функции для обратного вызова, да, но он потеряет контекст (this). Оказывается, что когда вы вызываете объект функции самостоятельно, то this является глобальным объектом. Проверьте это:

var obj = {
  foo: function() { console.log(this) }
};

obj.foo() // logs obj

var func = obj.foo;
func() // logs window (the global object in a browser)

Таким образом, вы не можете передать объект функции напрямую для обратного вызова, который должен вызываться как метод с объектом в качестве получателя. Внутри метода animate() он выполняет для вас callback(), то есть вызов, который не сохраняет для вас значения this.

Так почему же это не сработало?

    $('#element').animate(
        {'marginLeft': '50px'},
        'slow',
        function() {
            this.methodTwo(); // assuming 'this' refers to 'myObject'
        }
    );

Когда анонимная функция вызывается в качестве обратного вызова, как при разрыве метода, this по умолчанию используется для оконного объекта. Так что этот код на самом деле вызывает window.methodTwo(), который не существует, и он взрывается.

Итак, принятый стандартный JS способ сделать это вашим первым способом.

someFunc(arg1, arg2, function() {
  someObj.someMethod()
});

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

Изучение того, как this работает в JS, является болезненным опытом, но когда вы его получите, вы обнаружите, что правила довольно просты и просты в обращении.


Если вам все еще не нравится это, вы можете сделать некоторую хитрую магию js. Как и underscore.js метод bind () .

someFunc(arg1, arg2, _.bind(someObj.someMethod, someObj));

Возвращает функцию, которая всегда будет работать с someObj как this, которую можно безопасно использовать в качестве обратного вызова.

Или, если вы хотите попробовать CoffeeScript, у него есть жирная стрелка для сохранения контекста, которая компилируется в JS аналогично тому, что делает метод underscore.js bind().

someObj =
  foo: ->
    someFunc arg1, arg2, =>
      this.bar()

  bar: ->
    alert 'got the callback!'

someObj.foo()

В этом случае объявление функции в стиле => сохраняет контекст области, в которой оно появляется, поэтому его можно безопасно использовать.

1 голос
/ 17 августа 2011

Когда вы помещаете обратный вызов как myObject.methodTwo(), вы на самом деле вызываете methodTwo тогда и там. Затем внутри methodTwo он делает то же самое, вызывая methodOne ... что вызывает methodTwo, что вызывает methodOne и так далее. Итак, у вас бесконечная рекурсия.

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

1 голос
/ 17 августа 2011

когда вы даете jquery функцию для выполнения, вы фактически передаете указатель функции, а не то, что вы хотите выполнить ... она в основном выполняет exec для ссылки на функцию, которую вы передали, и, следовательно, когда вы изменили ее на obj.method ( ) это дольше это понимало ..

Проблема с myObject.methodTwo () заключается в том, что myObject.methodTwo () оценивается перед передачей в качестве функции. Javascript и, соответственно, jQuery ожидают указатель на функцию в подобных случаях. И.Е. Функция setTimeout.

В приведенном ниже использовании создается анонимная функция (просто блок операторов) и регистрируется как функция обратного вызова. Обратите внимание на использование 'function () {'. Анонимная функция делает только одно: вызывает myObject.methodOne ()

function() {
  myObject.methodOne();
}

касательно второго вопроса в функции обратного вызова. Это относится к другому объекту, а не к исходному объекту, и именно поэтому это не удалось. если вы хотите передать эту ссылку в функцию обратного вызова, прочитайте это: http://thomasdavis.github.com/tutorial/anonymous-functions.html

0 голосов
/ 17 августа 2011

This.methodTwo здесь ищет локальную ссылку. Это относится к текущей области видимости, поэтому второй метод определяет внешнюю область видимости, которую можно использовать. Я просто быстро просматриваю код и нахожу следующее предложение.

 methodOne: function()
  {
 var that = this;
    $('#element').animate(
        {'marginLeft': '50px'},
        'slow',
        function() {
            that.methodTwo(); // assuming 'this' refers to 'myObject'
        }
    );

}
0 голосов
/ 17 августа 2011

Как заявили zzzz и Пол, вы вызываете функцию и передаете ее возвращаемое значение, а не передаете обратный вызов. Но вам не нужно оборачивать это в анонимную функцию, чтобы она работала - просто удалите () после метода, и он будет работать нормально.

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