考虑使用对象文字定义的以下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
而感到非常震惊。我的猜测是我在某处失去了范围,但我不能正确地决定在哪里。非常感谢您的见解!
答案 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'
}
);
}