如果您的选择器对象无效,为什么 jQuery 不会崩溃?

最近使用了一些代码

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

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

查询的结果很简单,它没有执行。

显然这是设计好的,谁能解释一下为什么要选择这个设计,而不是提出一些例外?

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

6551 次浏览

这是 jQuery (我猜是 John Resigs)关于库哲学的一部分。

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

但是像往常一样,你可以很容易地扩展它:

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

但正如 Nick在评论中所说,大多数情况下,这是 没有期望的行为。如果出于某种原因想要使用它,那么应该使用上面这样的代码片段。

因为 jQuery 中的 $("#thisdivdoesntexist")仍然返回‘ em pty’jQuery Object,并且所有 jQuery 对象都有自己的方法,所以没有错误。

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

因此您可以在全局范围内使用选择器,即使有些页面没有选择器,也不必担心这个问题。

我认为这可能与你的选择者:

$("#divMenuContainer:visible")

在表面之下返回一个 jQuery 对象,其中包含许多可能的匹配项。然后对其中的每一个执行 Hide 函数。我想在这种情况下不抛出异常是有一定意义的,因为您有一个零条目的列表,而不是得到一个空值。

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

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();
});

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

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

通常,我只有在元素存在的情况下才继续执行下列操作:

var aThing = $("#myElement");
if(aThing.length){
//my code here
}

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

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

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

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

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

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

即使没有子节点,我们也可能希望事后跳回链中,继续使用 .prevObject引用返回链中。

有几十个这样的独特案例显示了图书馆现状的好处。至于 为什么,从对 John Resig(jQuery 的创建者)的采访中,他说这就是它的工作原理。他所追求的代码尽可能的简洁,链接模型就是从帽子里冒出来的,它恰好也有很多好处,上面的例子只是其中的一小部分。

需要澄清的是,我并不是说链接的 每个属性是一个好的属性,只是它有很多优点。


让我们以这一页为例,如果我们有这样的东西:

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

是否应该因为还没有任何注释而失败?不,不是真的,这是意料之中的,我不希望这里有错误... 如果元素存在做它,如果不做它。我的观点是,至少根据我的经验,因为缺少一个元素而抛出一个错误的 没有比抛出一个错误要有用得多。

你的问题中的选择器,#ID选择器是一个非常特殊的情况,你期望只有一个元素,所以也许你可以认为它应该在那里失败... 但那样就不会与其他选择器一致,你希望一个库是一致的。

对于几乎任何其他的 选择器,您都期望0-many 元素,所以在大多数情况下,如果没有找到任何元素,那么失败将是非常不理想的,在上面的 .live()中更是如此。

把它想象成 疑问就是这样。您要求所有符合条件的“记录”(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>

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

$("input:checked")

... 你不知道有多少被检查。可能是任何,全部或没有。这取决于用户。

所以,我们不需要像这样编写代码

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

你可以

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

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

我猜想因为它将被用于前端用户不应该看到异常,因为开发人员已经写了一些糟糕的代码。在这种情况下,悄无声息地失败可能更安全。

这是灵活性的问题。我自己也希望得到你要求的保护,你可以自己去做。用途:

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


return this;
};

现在使用 $(“ input: check”) . single () ,并保证它返回单个条目或者给出一个错误。

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