6

我经常在 JavaScript 中看到类似下面的内容:

$("#sendButton").click(function() {
    sendForm();
}

为什么有必要将调用包装在sendForm()函数内部?我认为这样做会更具可读性和更少的打字。

$("#sendButton").click(sendForm);

每种方法的优点/缺点是什么?谢谢!

4

6 回答 6

7

通常有两种情况,您希望使用前者而不是后者:

  1. 如果您需要在调用函数之前对参数进行任何后处理。

  2. 如果你在一个对象上调用一个方法,如果你使用第二种形式,作用域(这个引用)会有所不同

例如:

MyClass = function(){
    this.baz = 1;    
};

MyClass.prototype.handle = function(){
    console.log(this.baz);    
};

var o = new MyClass();

$('#foo').click(o.handle);
$('#foo').click(function(){
    o.handle();
});

控制台输出:

undefined
1
于 2013-01-06T23:04:48.037 回答
2

现在可能答案太多了,但两者之间的区别在于 的值this,即范围,进入sendForm。(论点也会有所不同。)让我解释一下。

根据 JavaScript 规范,调用这样的函数:sendForm();调用没有上下文对象的函数。这是给定的 JavaScript。

但是,当您将函数作为参数传递时,例如:$(...).click(sendForm),您只需传递对该函数的引用以供以后调用。您还没有调用该函数,而只是像对象引用一样传递它。仅当函数跟随它们时才调用函数( and()除外,稍后讨论)。在任何情况下,如果最终有人调用此函数,那么有人可以选择调用该函数的范围。callapply

在我们的例子中,那个人是 jQuery。当您将函数传递给 时$(...).click(),jQuery 稍后将调用该函数并将范围 (this) 设置为单击事件的 HTML 元素目标。你可以试试:$(...).click(function() { alert(this); });,会得到一个代表 HTML 元素的字符串。

因此,如果你给 jQuery 一个匿名函数的引用,上面写着sendForm(),jQuery 将在调用该函数时设置作用域,然后该函数将在sendForm没有作用域的情况下调用。本质上,它将清除this. 试试看:$(...).click(function() { (function() { alert(this); })(); });。在这里,我们有一个匿名函数调用匿名函数。我们需要内部匿名函数周围的括号,以便 () 应用于该函数。

相反,如果你给 jQuery 一个对命名函数的引用sendForm,jQuery 将直接调用这个函数并给它它承诺总是给的范围。

所以你的问题的答案现在变得更加明显:如果你需要this在开始工作时指向点击的元素目标,请sendForm使用.click(sendForm). 否则,两者都可以正常工作。你可能不需要this,所以跳过匿名函数。

对于那些好奇的人,可以通过使用 JavaScript 标准来强制作用域,apply或者call请参阅此处了解两者之间的差异)。使用点运算符时也会分配范围,例如 in: obj.func,它要求 JavaScript 调用this指向的函数obj。(所以理论上你可以obj在调用函数时强制成为作用域,方法是:obj.foo = (reference to function); obj.foo(); delete obj.foo;但这是使用 apply 的一种非常丑陋的方式。

jQuery 使用Functionapply调用具有范围的单击处理程序,也可以在函数调用上强制参数,事实上 jQuery 确实将参数传递给它的单击处理程序。sendForm因此,这两种情况之间还有另一个区别:当您从匿名函数调用并且不传递任何参数时,参数(不仅是作用域)会丢失。

于 2013-01-07T00:56:32.450 回答
1

在这里,您定义了一个可以内联调用多个函数的匿名事件处理程序。调试很脏而且很难,但是人们这样做是因为他们很懒,而且他们可以。

它也可以像您的第二个示例(我如何定义事件处理程序)一样工作:

$("#sendButton").click(sendForm)

通过内联定义事件处理程序,您可以获得将事件数据传递给多个函数的能力,并且您可以将this范围限定为事件对象:

$("#sendButton").click(function(event) {
    sendForm();
    doSomethingElse(event);
    andAnotherThing(event);
    // say #sendButton is an image or has some data attributes
    var myButtonSrc = $(this).attr("src");
    var myData = $(this).data("someData");
});
于 2013-01-06T23:02:25.930 回答
1

如果您所做的只是调用sendForm,那么最后,您包含的两个示例之间没有太大区别。

$("#sendButton").click(function(event) {
    if(event.someProperty) { /* ... */ }
    else { sendForm({data: event.target, important: 'yes'}); }
}

然而,在上面的例子中,我们可以处理从 传递给回调的参数click(),但是如果 sendForm 函数已经具备处理这个的能力,那么没有理由不将 sendForm 作为回调参数,如果这真的是你的全部是做。

function sendForm(event) {
    // Do something meaningful here.
}

$("#sendButton").click(sendForm);

请注意,在程序中处理不同逻辑层的位置取决于您;您可能已将某些通用功能封装在一个sendForm函数中,然后将其传递给此类函数,该函数在调用自身sendFormCallback之前处理事件/回调处理的临时业务。sendForm

如果您在回调密集的环境中工作,明智的做法是将重要功能与回调触发器本身分开,以避免回调地狱并提高源代码的可维护性和可读性。

于 2013-01-06T23:12:56.980 回答
1

这只是锁定范围。当您将其包装sendForm()在关闭当前范围的匿名函数中时。换句话说,this遗嘱将被保留。如果您只是通过sendForm,那么任何调用都this将来自调用范围。
这是一个学习 javascript 范围和质疑约定的好问题。

于 2013-01-06T23:26:46.957 回答
0

不,第二个例子是完全有效的。

99.9% 的 jQuery 示例使用第一种表示法,这并不意味着您需要忘记基本的 JavaScript 语法。

于 2013-01-06T23:00:26.663 回答