jQuery,Javascript,Object Notation - 回调行为差异的解释

时间:2011-08-16 22:02:33

标签: javascript jquery oop

考虑使用对象文字定义的以下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
        );

    },

...同时methodOnemethodTwo会导致浏览器错误,指出too much recursion。我如何宣布回调之间的区别是什么?另外,如果我带回匿名函数声明,但修改对象引用看起来像这样......

    methodOne: function()
    {

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

    },

...我通过methodOne获得了一次良好的传递,在回调时,我的浏览器因为无法找到methodTwo而感到非常震惊。我的猜测是我在某处失去了范围,但我不能正确地决定在哪里。非常感谢您的见解!

5 个答案:

答案 0 :(得分:2)

让我们打破这个

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

在此示例中,您将函数对象作为回调传递。动画完成后调用该函数对象。它具有通过闭包共享的myObject对象,因此它可以轻松找到它并在其上调用方法。真棒!

    $('#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对象。所以这段代码实际上调用了window.methodTwo(),它不存在,并且它会爆炸。

所以公认的标准JS方式是你的第一种方式。

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

这应该始终有效,即使它因为你调用2个函数做一件事似乎浪费了。但是当你发现它是最不容易出错的时候。

了解this在JS中的工作原理是一种痛苦的经历,但是当你得到它时,你会发现这些规则非常直接且容易操作。


如果你仍然不喜欢,你可以做一些棘手的js魔法。与underscore.js bind() method一样。

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 :(得分:1)

当你给jquery执行的函数你实际上是传入一个函数指针,而不是你要执行的东西..它基本上对你传递的函数引用执行,因此当你把它改为obj.method时( )它更久了解它..

myObject.methodTwo()的问题在于myObject.methodTwo()在作为函数传递之前被计算。 Javascript和扩展jQuery期望在这样的情况下的函数指针。 I.E. setTimeout函数。

在下面的用法中,创建了一个匿名函数(只是一个语句块)并注册为回调函数。注意使用'function(){'。匿名函数完成一件事:调用myObject.methodOne()

function() {
  myObject.methodOne();
}

关于你在回调函数中的第二个问题,这指的是一个不同的对象而不是你的原始对象,这就是失败的原因。如果你想将这个引用传递给回调函数,请阅读:http://thomasdavis.github.com/tutorial/anonymous-functions.html

答案 2 :(得分:1)

当您将回调设置为myObject.methodTwo()时,您实际上正在调用 methodTwo。然后在methodTwo内部通过调用methodOne ...调用methodTwo调用methodOne来执行同样的操作,依此类推。所以你有一个无限的递归。

当你在匿名函数中包含同一行代码时,它立即调用该函数。您现在正在传递函数本身,而不是返回的值。然后使用回调的函数确定何时(如果有的话)实际调用该函数。

答案 3 :(得分:0)

正如zzzz和Paul所说,你正在调用函数并传递其返回值,而不是传递回调。但是你不需要在匿名函数中包含它以使其工作 - 只需在方法之后删除()它就可以正常工作。

答案 4 :(得分:0)

This.method这里是寻找本地参考,这属于当前范围,所以方法二是定义你可以使用的当前范围。我只是快速查看代码并找到以下建议。

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

}