为什么不建议使用"$().ready(handler)"?

88
jQuery API文档站点ready获取的以下内容:

以下三种语法是等效的:

  • $(document).ready(handler)
  • $().ready(handler) (不建议使用此方法)
  • $(handler)

在阅读并尝试源代码后,我不知道为什么。

$().ready(handler) 

不建议使用。第一种和第三种方式是完全相同的,第三种选项在一个已缓存的 jQuery 对象上使用了 document 调用了 ready 函数:

rootjQuery = jQuery(document);
...
...

// HANDLE: $(function)
// Shortcut for document ready
} else if ( jQuery.isFunction( selector ) ) {
    return rootjQuery.ready( selector );
}

但是ready函数与所选节点元素的选择器没有交互。下面是ready的源代码:

ready: function( fn ) {
    // Attach the listeners
    jQuery.bindReady();
        // Add the callback
    readyList.add( fn );
        return this;
},

正如您所看到的,它只是将回调函数添加到内部队列(readyList)中,并不会更改或使用集合中的元素。这使您可以在每个jQuery对象上调用ready函数。

例如:

  • 常规选择器:$('a').ready(handler) 演示
  • 无意义选择器:$('fdhjhjkdafdsjkjriohfjdnfj').ready(handler) 演示
  • 未定义选择器:$().ready(handler) 演示

最后... 我的问题是:为什么不推荐使用$().ready(handler)


60
在我看来,他似乎力求把使用的工具烂熟于心。我并不认为这是白白浪费的努力。 - Jon
5
好问题。如果有人感兴趣,这里有一个性能比较网址……它展示了(至少在Chrome中)那个“不推荐使用”的版本实际上是最快的。 - James Allardice
5
更好的问题是为什么这些东西存在,它应该是一个静态方法(例如$.ready),而不需要首先构造一个jQuery对象。 - Esailija
2
@Esailija提出了最好的观点。除非jQuery计划为单个元素提供某种.ready()功能,否则构造jQuery对象是没有必要的。 - user1106925
2
@ChaosPandion。你不能使用那个... $.ready 已经被内部jQuery函数占用了,搜索源代码寻找 ready: - gdoron
显示剩余15条评论
6个回答

89

我从jQuery开发人员那里得到了正式答案:

$().ready(fn)之所以有效,是因为在早期版本的jQuery中$()曾经是$(document)的快捷方式(jQuery <1.4)。因此,$().ready(fn)是一种易读的代码。

但是人们过去常常使用$().mouseover()和其他各种疯狂的方法,而人们必须使用$([])获得一个空的jQuery对象。

因此,在1.4中,我们将$()更改为提供一个空的jQuery对象,并且我们只是让$().ready(fn)起作用,以避免破坏大量代码。

$().ready(fn)现在在核心中只是被修补过来以使它在旧版本中能够正常工作。

ready函数最好放在$.ready(fn)中,但这是一个非常古老的设计决策,而现在我们只有这个。


我问他:

你觉得$(fn)比$().ready(fn)更易读吗?!

他的回答是:

在实际应用程序中,我总是使用$(document).ready(fn),通常只有一个文档就绪块在应用程序中,这不完全是维护的事情。

我认为$(fn)也很难读懂,它只是一种你必须知道如何工作的东西...


1
有道理,jQuery非常注重向后兼容性。 - Esailija
@Esailija:如果他们真的那么认真,他们就不会在第一次改变$()的行为(尽管这种行为可能很荒谬)。另一方面,你是对的。他们并不总是倾向于进行重大更改,就像当他们试图改变.attr()时所表现的那样,然后几天后迅速恢复了原状。这使他们受到了一些不幸的早期(和中期)设计决策的限制。 - user1106925
3
@ gdoron +1 意味着直接从消息源头获得正确信息。 - user1106925
2
@gdoron 真正回答得很好,加1分。是的,我们的观点非常接近。 - VisioN
“这只是你必须知道它如何工作的事情™……” $(selector[, context])$(html[, ownerDocument]) 也是如此。实际上,如果必须知道它如何工作是问题所在,那么最好直接使用 jQuery() 而不是 $()。或者为什么要使用 jQuery 呢? - JAB

11

既然你已经指出不同的选项几乎做着相同的事情,那么现在是时候换上库作者的帽子并进行一些猜测了。

  1. 也许jQuery的开发者希望将$()保留以备将来使用(虽然有疑问,因为$().ready被记录为可用,即使不推荐使用;如果特殊处理会污染$的语义)。

  2. 一个更实际的原因:第二个版本是唯一一个不会包装document的版本,因此在维护代码时更容易出错。例如:

    // BEFORE
    $(document).ready(foo);
    
    // AFTER: works
    $(document).ready(foo).on("click", "a", function() {});
    

    将此与之对比:

    // BEFORE
    $().ready(foo);
    
    // AFTER: breaks
    $().ready(foo).on("click", "a", function() {});
    
  3. 与上述相关:ready在某种意义上是一个怪物,因为它是(唯一的?)一个无论 jQuery 对象包装了什么内容(即使它没有包装任何东西,就像这里一样),都能以相同方式工作的方法。这与其他 jQuery 方法的语义有很大的不同,因此特别依赖它是不被鼓励的。

    更新:正如 Esailija 的评论所指出的那样,从工程的角度来看,ready 应该真正成为一个静态方法,因为它的工作方式就是这样。

更新 #2:通过查看源代码,似乎在 1.4 分支的某个时刻,$() 被更改为匹配 $([]),而在 1.3 中它的行为类似于 $(document)。这个变化会加强上述的理由。


我从未见过这样的代码,旧的习语是 $(document).ready( function(){ //your code here } ); - Esailija
我无法理解第二次更新,请您能否再详细说明一下?我搜索了旧版本,但是对于这个空的jQuery对象问题没有发现任何差异。 - gdoron
@gdoron:我的意思是从selector = selector || document改为if(!selector) return this的变化。 - Jon

4

我认为问题在于$()返回的是一个空对象,而$(document)不是,因此你在应用ready()到不同的东西上;它仍然能正常工作,但我认为这并不直观。

$(document).ready(function(){}).prop("title") // the title
$().ready(function(){}).prop("title")  //null - no backing document

1
是的,这个答案中有相同的看法。因此,变化发生在版本1.4中,该版本发布了新的返回政策。 - VisioN
不能通过 $() 链接任何文档属性或方法。 - Alex K.
2
@AlexK的观点是,实际上没有人会在.ready后面加上链式操作,因为这已经成为一种很好的编程习惯。当然,理论上可能会有人这样做,但我从来没有见过这样的代码(虽然这不是一个好的论点)。 - Esailija
2
也许正因为这种“理论上的机会”,该方法并不被推荐,因为它在不同版本中具有不同的行为。 - VisioN
@Esailija。我认为不更改文档也没问题,你没有将文档作为“后备字段”传递,为什么要更改它呢? - gdoron
显示剩余2条评论

3
很可能这只是一种文档错误,应该予以修正。使用$().ready(handler)的唯一缺点在于其可读性。当然,可以认为$(handler)同样难以理解。我同意这个看法,这也是我不使用它的原因
你也可以认为一个方法比另一个方法更快。然而,在同一页上连续调用此方法多少次才能注意到差异呢?
最终,它取决于个人喜好。除了可读性的争议之外,没有使用$().ready(handler)的缺点。在这种情况下,我认为文档存在误导。

+1!你说得完全正确!你会喜欢阅读官方jQuery答案的。我已经将其添加为答案。 - gdoron

2

为了显而易见地表明这三个存在一些不一致性,同时我添加了第四种常用形式:(function($) {}(jQuery));

使用此标记:

<div >one</div>
<div>two</div>
<div id='t'/>

而这段代码:

var howmanyEmpty = $().ready().find('*').length;
var howmanyHandler = $(function() {}).find('*').length;
var howmanyDoc = $(document).ready().find('*').length;
var howmanyPassed = (function($) { return $('*').length; }(jQuery));
var howmanyYuck = (function($) {}(jQuery));
var howmanyYuckType = (typeof howmanyYuck);

$(document).ready(function() {
    $('#t').text(howmanyEmpty + ":" + howmanyHandler + ":" 
        + howmanyDoc + ":" + howmanyPassed + ":" + howmanyYuckType);
});
上一条语句中div的显示结果为:0:9:9:9:undefined
因此,只有Handler和Doc版本符合jQuery约定,它们获取文档选择器并返回有用的内容。在Passed表单中,必须返回某些内容(我不会这样做,但只是为了展示“内部”有些东西而加入)。
以下是jsfiddle版本的链接,供感兴趣的人参考:http://jsfiddle.net/az85G/

我看不出有什么问题,它找不到任何东西,你提供的jQuery上下文是“null”,所以.find('*').length返回0。你觉得这(显而易见的)行为有什么问题吗? - gdoron
@gdoron - 我并不认为这种行为有什么不好,我只是想指出与具有非空选择器时的差异 - 请注意,这个空选择器可能是其他地方被标记为“更快”的评论的原因,因为它有一个较小的对象要处理,但在那种情况下确实返回一个对象而不是“未定义”。不过,我真的很喜欢这个问题,并且已经点赞了 :) - Mark Schultheiss
它更快的原因是因为构造函数的第一个“break”条件是if(!selector) return this,如果你提供其他东西,则会引发regex等其他操作......感谢你友好的话语......我想我可能会问jQuery团队来回答这个问题(见鬼,这不是我的库 :-))。 - gdoron
是的,我没有学习过代码库中的这个特定部分,我已经破解了核心以放置临时修复程序来修复错误,但不包括它。目前,我确实更喜欢在我们的代码库中看到 jQuery(document).ready(function(){}); 的形式,因为有不同水平的 jQuery 专业知识,对于新手来说,它是“最明显”的 jQuery 事件处理函数。 - Mark Schultheiss

0

我认为这更多是为了可读性而不是其他任何事情。

这个不太具有表现力

$().ready(handler);

作为

$(document).ready(handler)

或许他们正在尝试推广某种惯用的jQuery。


3
$(document).ready(handler)$(handler)更易读,但建议使用$(handler)... - gdoron

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接