如果你的选择器对象无效,为什么jQuery不会轰炸?

Why doesn't jQuery bomb if your selector object is invalid?

提问人:Maxim Gershkovich 提问时间:9/14/2010 最后编辑:AbyxDevMaxim Gershkovich 更新时间:12/3/2018 访问量:6649

问:

最近使用了一些代码,大致如下

$("#divMenuContainer:visible").hide("explode");

然而,在花了一些时间试图让它工作之后,我意识到我的选择器引用了一个不存在的 div。

查询的结果只是它没有执行。

显然这是设计使然,谁能解释为什么做出这种设计选择而不是提出某种例外的逻辑?

不是试图批评,只是试图理解。

javascript jquery jquery-selectors

评论

13赞 David 9/14/2010
同意,我喜欢这样的问题。“这就是我这样做时发生的事情......为什么?“比”坏了,修好它!“有趣得多。
0赞 jrharshath 9/14/2010
可能在jQuery邮件列表上问这个问题会得到更明智的答案。除非 Jeresig 在这里:)
7赞 Nick Craver 9/14/2010
@Here - 他是:stackoverflow.com/users/6524/john-resig
1赞 Josh Stodola 9/14/2010
我不知道为什么,但我当然很高兴它确实如此!
3赞 mjc 7/17/2011
这正是我写这个小剧本的原因:github.com/mikeycgto/jquery.whiny.js

答:

5赞 jAndy 9/14/2010 #1

这是jQuerys(我猜实际上是John Resigs)关于库的哲学的一部分。

它试图对你和你的客户“友善”,这反过来意味着,它很少
会抛出异常(很少)

但像往常一样,您可以轻松地扩展它,例如:

(function(_jQuery){
    jQuery = function(){
        var ret = _jQuery.apply(this, arguments);
        if(!ret.length) throw new Error('empty selector');
        return ret;
    };
}(jQuery));

但就像在评论中所说的那样,大多数时候这不是一种理想的行为。如果你出于某种原因想要拥有它,像上面这样的代码片段应该可以做到。Nick

评论

2赞 Konrad Rudolph 9/14/2010
有了这样的开发理念,就必须有两种模式,一种是宽松的(生产),另一种是早期和经常抛出的(用于调试)。否则,一个理智的人怎么能做任何调试呢?我根本不了解jQuery——也许存在这样的模式?
0赞 rfunduk 9/15/2010
@Konrad:只需将上述代码放入文件“debug.js”中,并且仅在开发过程中将其包含在源代码中。这种事情是一种常见的模式。
2赞 Shog9 9/16/2010
@Konrad:不匹配的选择器本身并不是一个错误。这不像 HTML,那里有(合理地)严格的标准和宽松的浏览器,不强制遵守 - 空集在许多(如果不是大多数)程序中是一种有用且依赖的行为。如果选择器不匹配,程序会出现故障,那么有责任验证它是否匹配。
0赞 Gerry 6/14/2012
@Shog9:这不是问题所在。问题是:为什么这根本行不通???...x 次后:哦,妈的,我有一个字符错别字,我的选择器实际上没有匹配任何东西;或者我不小心在我的 HTML 中设置了一个类而不是 ID,并且一直以来我一直在尝试匹配 ID。
2赞 Stefanvds 9/14/2010 #2

因为在jQuery中仍然返回一个“空”jQuery,并且所有jQuery对象都有自己的方法,所以没有错误。$("#thisdivdoesntexist")Object

这其实是一件好事。如果这会引发错误,那么在执行某些操作之前,在很多情况下都需要进行一些额外的检查,这意味着您将有很多“重载”代码。尽管在对对象调用方法之前检查它是否存在并不是坏习惯,但当选择器不返回任何内容时,并非所有 JavaScript 都会停止(如果它抛出错误,就会发生这种情况)

因此,即使某些页面没有选择器,您也可以全局使用选择器,而不必担心这一点。

评论

0赞 gen_Eric 9/14/2010
是的,总是返回一个 jQuery 对象,在 的情况下,该对象有 0 个元素。$('something')$("#thisdivdoesntexist")
0赞 jAndy 9/14/2010
无论如何,这当然是对 的调用,但这可能至少应该选择性地抛出警告。jQuerys constructor function
0赞 Stefanvds 9/14/2010
你会如何发出警告?
0赞 Paddy 9/14/2010 #3

我认为这可能与您的选择器以下事实有关:

$("#divMenuContainer:visible")

在幕后返回一个 jQuery 对象,其中包含许多可能的匹配项。然后对其中每个执行隐藏功能。我想在这种情况下不抛出异常是有一定道理的,因为你有一个条目为零的列表,而不是得到一个空值。

3赞 recursive 9/14/2010 #4

不引用任何元素的选择器仍然是合法的选择器,它可能是有意的。给定的选择器有时可能会返回元素,并且您希望能够使用这样的选择器而不会引发运行时错误。

6赞 BGerrissen 9/14/2010 #5

jQuery() 将始终返回一个 jQuery 对象,以防止错误,但更重要的是:

因此,您可以编写响应式代码。

do X if Y is present

如果 Y 不存在,则不计算 X。

这意味着您可以拥有一个全局的 init 跨页面,并且无论它们是否找到某些东西,都可以使用 init 插件。

$(function(){
    // does nothing if the page contains no element with className 'accordion'
    $('.accordion').implementAccordion();
    // usually on a single page, but we can add it to a global js file nontheless.
    $('.validate-form').implementFormValidator();
});

当然,有些插件写得很差,会抛出错误。

0赞 gnarf 9/14/2010 #6

这对我来说很有意义......如果你的选择器与任何元素都不匹配,你仍然可以调用所有普通的jQuery原型函数,而不会产生错误!最坏的情况是,你最终会得到一个“空”的jQuery集,将你的更改应用于任何元素。

1赞 Chris Barr 9/14/2010 #7

通常,我只在元素存在时继续执行以下操作:

var aThing = $("#myElement");
if(aThing.length){
    //my code here
}
54赞 Nick Craver 9/14/2010 #8

这里有几个很好的理由,“可链接性”是主要驱动力,通过链接编写非常简洁的代码的能力必须不会抛出错误才能完美地工作,例如:

$("#divMenuContainer:visible").hide("explode").add("#another").fadeIn();

链中的每个对象,即使它没有引用任何 DOM 元素,也可能在以后添加更多元素,或者让我们再举一个例子:

$("#divMenuContainer:visible").live("click", function() { ... });

在这种情况下,我们不关心选择器找到的任何元素,我们关心选择器本身。这是另一个:

$("#divMenuContainer:visible").find(".child").hide("explode").end().fadeOut();

即使没有孩子,我们也可能想在之后跳回链中,继续使用引用来回到链上。.prevObject

像这样的例子有几十个不同的案例,显示了图书馆这样做的好处。至于原因,从jQuery的创建者John Resig的采访中,他说这就是它的结果。他追求的代码尽可能简洁,而链接模型就是从帽子中得出的,它恰好也有很多好处,上面的例子只是其中的一小部分。

需要明确的是,我并不是说链接的每个属性都是好的,它只是有很多好处。


让我们以这个页面为例,如果我们有这样的东西会怎样:

$(".comment").click(replyToFunction);

这应该因为还没有任何评论而失败吗?好吧,不,不是真的,这是意料之中的,我不希望这里出现错误......如果元素存在,则执行,如果不存在,则不执行。我的观点是,至少根据我的经验,因为缺少元素而抛出错误比抛出错误更有用。

你问题中的选择器,#ID选择器是一个非常特殊的情况,你只期望一个元素,所以也许你可以争辩说它应该在那里失败......但是,这与其他选择器不一致,并且您希望库保持一致。

对于几乎任何其他选择器,您都期望 0-many 元素,因此在大多数情况下,当您找不到任何元素时失败将大大不那么可取,在上述 .live() 等情况下更是如此。

评论

0赞 cthulhu 9/14/2010
这枚硬币的另一面是,如果你想在元素不存在的情况下做一些事情,你就会对空的选择器结果进行大量检查。当然,人们不想这样做。如果页面上的某个元素丢失了,而不应该丢失,则后端的输出是错误的。这可以通过测试某些条件下的输出来捕获。
9赞 Nick Craver 9/14/2010
@Cthulhu - 如果你想检查,这是一个很短的方法:)你也可以为这样的东西定义一个静态方法。if(!$("selector").length) { }
0赞 Matt Ball 9/15/2010
提及 null 对象模式可能很有用。
0赞 Nick Craver 9/15/2010
@Bears - 对于某些示例,它符合此模式,对于其他示例,例如我们甚至不关心其中的 DOM 元素......这并不是说列表是空的或空的,我们只是不在乎它。因此,有些链接的东西使用它,有些则不使用它,有时它们使用其他属性(在这种情况下,在其他情况下)。.live().selector.context.prevObject
3赞 Nick Craver 9/16/2010
@sfink - 当然,如果你愿意,它看起来像这样: 您可以在此处测试/使用它: jsfiddle.net/nick_craver/VrXmc 检查控制台是否有错误。jQuery.fn.require = ​function(num, num2) { if(this.length < num) $.error("At least " + num + " elements are required"); if(num2 && this.length > num2) $.error("Between " + num + " and " + num2 + " elements required"); };
56赞 Matt Sherman 9/14/2010 #9

把它想象成一个查询,它就是这样。您要求提供符合您条件的所有“记录”(DOM 元素)。结果是一组零条记录。

然后,它会遍历您的零记录,并将操作应用于它们。:)

如果你对 SQL 或数组做同样的事情,它在大多数语言中的行为方式都是一样的。零记录的集合不是错误状态。

var things = $("invalid selector");
$("p").text("The object is valid: " + things + " but has " + things.length + " elements.")
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<p></p>

评论

6赞 danp 9/14/2010
缺少一个对象与它的存在一样具有信息性;)只需要为此进行设计。
0赞 Gerry 6/14/2012
您所说的循环遍历一组记录的内容对标记和类有意义,但对于应该只有一个实例的 ID 没有意义。只是指出这一点。
3赞 CaffGeek 9/14/2010 #10

一个很好的例子是,当您想使用所有选中的复选框执行某些操作时

$("input:checked")

...你不知道检查了多少。可以是任何、全部或无。这取决于用户。

因此,不必编写像

var checkedInputs = $("input:checked");
if (checkedInputs  && checkedInputs .length > 0) {
      checkedInputs .doStuff();
}

你可以只是

$("input:checked").doStuff();

如果他们做出了选择,那就太好了,事情就完成了。如果不是...没有伤害,没有犯规。

评论

1赞 user113716 9/15/2010
仅供参考 - 您的陈述中的第一个测试将始终计算为 ,因此您可以简单地执行: .或者,由于任何大于 的数字都等于 ,您可以将其简化为 。:o)if()trueif(checkedInputs.length > 0) {...0trueif(checkedInputs.length) {...
0赞 PeterJ 9/14/2010 #11

我猜是因为它将在前端使用 - 最终用户不应该看到异常,因为开发人员编写了一些糟糕的代码。在这种情况下,静默失败通常更安全。

8赞 Frank Schwieterman 9/16/2010 #12

这是一个灵活性问题。我自己,我想要你要求的同样的保护,你总是可以自己做。用:

jQuery.fn.single = function() {
    if (this.length != 1) {
        throw new Error("Expected 1 matching element, found " + this.length);
    }

    return this;
};

现在使用 $(“input:checked”).single() 来保证它要么返回单个项目,要么给你一个错误。

0赞 JD Isaacks 9/21/2010 #13

我以为它就像CSS一样,这意味着如果你试图设置一个不存在的元素的样式,你不会得到任何错误。