何时使用原生JavaScript而不是jQuery?

250

我注意到在监控/尝试回答常见的jQuery问题时,有些使用JavaScript而不是jQuery的做法实际上可以让你写得更少却能够完成同样多的事情... 而且可能还会带来性能方面的好处。

一个具体的例子如下:

$(this)this之间的区别

在单击事件中引用被单击对象的id

jQuery

$(this).attr("id");

JavaScript

this.id;

还有其他类似的常见做法吗?可以更容易地完成某些JavaScript操作,而不必引入jQuery。还是这是一个罕见的情况?(即需要使用更多代码来实现jQuery“快捷方式”)

编辑:虽然我很感谢有关jQuery与纯JavaScript性能的答案,但我实际上正在寻找更加定量化的答案。在使用jQuery时,是否存在一些情况,实际上最好使用纯JavaScript而不是使用$(),以提高可读性/紧凑性。除了我在原始问题中给出的示例之外。


我不确定我理解这个问题。您是在寻求有关使用库代码的优点的意见,还是在寻找类似于this.id的其他非jQuery跨浏览器兼容技术? - user113716
1
从答案来看,似乎每个人都将这个问题视为哲学问题,而我本意是要非常量化地列出像我概述的那种常见(恶劣)做法的实例列表。 - jondavidjohn
78
相关的 http://www.doxdesk.com/img/updates/20091116-so-large.gif - goat
5
@chris 还有一个问题:在我看来,Meta已经成为了“另一种语言”的中心。当我来到这里时,我甚至不知道什么是Waffles、Unicorns或Pony。但是,随着时间的推移,我学会了它们,并且现在也能使用它们。你认为这是否是Meta的一部分?是否有必要让新用户为了了解这些“Memes”而花费时间和精力?如果是这样,他们应该如何被引导到相关的信息? - Tyler
13个回答

211
  • this.id(如您所知)
  • this.value(对于大多数输入类型而言没有问题。我只知道在IE中,如果一个<select>没有在其<option>元素上设置value属性,或者在Safari浏览器中使用单选按钮,则会出现问题。)
  • this.className 可以获得或设置整个“class”属性
  • this.selectedIndex 用于获取所选索引的<select>
  • this.options 用于获取<select>中的<option>元素列表
  • this.text 用于获取<option>的文本内容
  • this.rows 用于获取<table><tr>元素集合
  • this.cells 用于获取<tr>的单元格(td和th)
  • this.parentNode 用于获取直接父级
  • this.checked 用于获取checkbox的选中状态 感谢@Tim Down
  • this.selected 用于获取option的选中状态 感谢@Tim Down
  • this.disabled 用于获取input的禁用状态 感谢@Tim Down
  • this.readOnly 用于获取input的readOnly状态 感谢@Tim Down
  • this.href 对于一个<a>元素,用于获取其href
  • this.hostname 对于一个<a>元素,用于获取其href的域名
  • this.pathname 对于一个<a>元素,用于获取其href的路径
  • this.search 对于一个<a>元素,用于获取其href的查询字符串
  • this.src 对于一个可以有src的元素

......我想你已经明白了。

有时候性能非常关键。比如说如果您要在循环中多次执行某些操作,您可能需要放弃jQuery。

一般情况下,您可以将:

$(el).attr('someName');

上面的表述不太清晰。 getAttribute 不是一种替代方法,但它可以检索从服务器发送的属性值,并且相应的 setAttribute 将其设置。在某些情况下是必需的。

下面的句子已经涉及到这个问题了。 看看这个答案,更好地解释了这个问题。

el.getAttribute('someName');

为了直接访问属性,可以使用点语法。请注意,属性与属性(有时相互映射)不同。当然也有 setAttribute 方法。

假设您收到一个需要解包特定类型的所有标签的页面。使用 jQuery 很简单:

$('span').unwrap();  // unwrap all span elements

但如果有很多的话,您可能想使用一些本地 DOM API:

var spans = document.getElementsByTagName('span');

while( spans[0] ) {
    var parent = spans[0].parentNode;
    while( spans[0].firstChild ) {
        parent.insertBefore( spans[0].firstChild, spans[0]);
    }
    parent.removeChild( spans[0] );
}

这段代码非常短,比jQuery版本的性能更好,并且可以轻松地制作成一个可重复使用的函数放入个人库中。

外部的while语句看起来可能会产生无限循环,因为它使用了while(spans[0]),但是由于我们处理的是“实时列表”,当我们执行parent.removeChild(span[0]);时,它会得到更新。这是一个很棒的功能,在使用类似数组的对象时却无法享受到。


3
很好...所以大部分都是因为这个关键词被不必要地包装成了jQuery对象。 - jondavidjohn
1
@jondavidjohn:嗯,在某些情况下,有一些修复措施是不错的,比如在某些情况下存在一些浏览器怪异行为时使用this.value。这完全取决于您的需求。在没有jQuery的情况下,有许多很好的技巧,特别是当性能至关重要时。我会尝试想出更多的方法。 - user113716
3
我本打算写一个答案,但现在我会给你一些建议。通常情况下,“attr()”被过度使用了。如果你只需要处理一个元素,那么很少需要使用“attr()”。我最喜欢的两个例子是复选框的“checked”属性(关于这个属性有各种神秘的咒语:“$(this).attr("checked", "checked")”,“$(this).is(":checked")”等)以及<select>元素的“selected”属性。 - Tim Down
1
啊啊啊,@patrick,不要啊:你建议使用getAttribute()你几乎从不需要它:它在IE中无法正常工作,有时无法反映当前状态,而且这也不是jQuery的做法。只需使用属性即可。https://dev59.com/yFLTa4cB1Zd3GeqPcrLh#4456805 - Tim Down
1
我使用JS很长时间了,但我不知道className这个属性,谢谢。 - IAdapter
显示剩余10条评论

66
正确答案是,使用jQuery而不是“普通的”本机JavaScript,您将始终承担性能损失。这是因为jQuery是JavaScript库,而不是一些新鲜的、花哨的JavaScript版本。
jQuery强大的原因在于它使一些在跨浏览器情况下过于繁琐的事情(AJAX是最好的例子之一)变得简单,并解决了众多可用浏览器之间存在的不一致性并提供了一致的API。它还轻松地实现了链式编程、隐含迭代等概念,以便更轻松地同时对元素组进行操作。
学习jQuery不能替代学习JavaScript。您应该要有牢固的基础,这样您才能充分理解掌握jQuery所能带来的便利。
-- 编辑以涵盖评论 --
正如评论中快速指出的(我完全同意),上述语句是针对基准测试代码而言的。一个“本机”的JavaScript解决方案(假设它编写良好)在几乎每种情况下都会比完成相同任务的jQuery解决方案执行得更快(我很想看到反面例子)。jQuery确实加快了开发时间,这是一个重大的好处,我不想降低它的价值。它使代码易于阅读、易于跟踪,而这些是一些开发人员自己无法轻松创建的。
因此,在我看来,答案取决于您试图实现什么目标。如果根据您提到性能优势的引用,您想要从应用程序中获得最佳速度,则每次调用$()时使用jQuery会引入开销。如果您追求可读性、一致性、跨浏览器兼容性等方面,则有充分的理由支持jQuery而不是“原生”的JavaScript。

8
我很乐意看到一个jQuery比纯JS实现更优的情况示例。无论你做什么,如果你在使用jQuery(或其他任何库),都会有不可避免的函数开销。调用"$()"生成的虽然是小的开销,但却无法避免。如果你不进行这个调用,你就可以节省时间。 - g.d.d.c
17
一个写得不好的自制解决方案可能会更慢。jQuery团队在这个套件中执行了一些高效的JavaScript。请参见@Freelancer的回答。 - Stephen
4
一篇写得很糟糕的解决方案永远都是一篇写得很糟糕的解决方案,这与编程语言无关。这并不改变一个库函数会产生开销的事实,如果精心设计,一个“本地”的函数可以避免这种情况。 - g.d.d.c
11
@g.d.d.c. 我认为 jQuery “超越”(即在基准测试之外)纯 JS 的最好例子是缩短开发时间(从商业角度来看,这是一个巨大的价值)和简化实验。 - Ben
13
你所写的一切都是正确的,但你忽略了@Ben在上面提到的内容。jQuery使开发人员更快,更健壮。请参见我多年前编写并经常使用的KillClass()实现。你知道吗?对于某些极端情况,它是有问题的。糟糕的代码是糟糕的代码,错误的代码是错误的代码,但通常使用jQuery可以使开发人员编写更好、更正确的代码。大多数时候,计算机已经足够快了;需要帮助的是开发人员。 - Phrogz
显示剩余3条评论

41

有一个名为...噢,猜猜看?Vanilla JS的框架。希望你能理解这个笑话... :D 它为性能而牺牲了代码可读性... 将其与下面的jQuery进行比较,你会发现通过ID检索DOM元素几乎快了35倍。:)

因此,如果你想要性能,最好尝试使用 Vanilla JS 并得出自己的结论。也许你不会遇到 JavaScript 在密集的代码(如 for 循环)内挂起浏览器的 GUI / 锁定 UI 线程。

Vanilla JS 是一个快速、轻量级、跨平台的框架,用于构建令人难以置信的,功能强大的 JavaScript 应用程序。

在他们的主页上有一些性能比较:

enter image description here


1
@Leniel Macaferi 哦,老兄,我喜欢这个答案!我没有意识到纯净版和其他框架之间的性能差异会产生如此大的影响!无论如何,这个答案值得入选名人堂!附言:你也制作了这个网站吗? - Steve

18

已经有一个被接受的答案,但我认为没有直接在此处输入的答案可以在本地JavaScript方法/属性列表方面提供全面保证跨浏览器支持。为此,我可以将您重定向到quirksmode:

http://www.quirksmode.org/compatibility.html

这可能是任何地方都可以找到的最全面的什么在哪个浏览器上工作以及什么不起作用的列表。特别注意DOM部分。需要阅读的内容很多,但重点不是读完所有内容,而是把它用作参考。

当我开始认真编写Web应用程序时,我打印了所有DOM表格并将它们悬挂在墙上,以便一眼就知道什么是安全使用的,什么需要黑客手段。现在,我只需在怀疑时像quirksmode parentNode compatibility这样搜索即可。

像其他任何事情一样,判断主要是经验问题。我不会真正建议您阅读整个站点并记住所有问题,以弄清何时使用jQuery和何时使用普通JS。只需了解该列表即可。搜索起来很容易。随着时间的推移,您将培养出更喜欢使用普通JS的直觉。


PS:PPK(该网站的作者)还有一本非常好的书,我建议阅读


14

何时使用JavaScript,何时使用jQuery:

  1. 当你知道你所做的事情有无与伦比的跨浏览器支持,
  2. 它所需的代码量不多,
  3. 它不会显著降低可读性,
  4. 你相信jQuery不会选择不同的实现方式以在不同的浏览器上获得更好的性能时,

使用JavaScript。否则使用jQuery(如果可以)。

编辑:该答案适用于总体上使用jQuery还是不使用它,以及选择在jQuery内部使用纯JS之间的区别。在选择使用attr('id').id之间时,倾向于使用JS,而在选择使用removeClass('foo') vs .className = .className.replace( new Regexp("(?:^|\\s+)"+foo+"(?:\\s+|$)",'g'), '' )之间时,倾向于使用jQuery。


3
我认为提问者打算使用jQuery,但想知道何时何地在jQuery方法中使用本地JavaScript。 - Stephen
.classList.remove('foo') 在更新的浏览器中似乎运行良好。 - Tyilo
3
classList.remove是HTML5 ClassList API的一部分,在IE9中没有实现,例如http://caniuse.com/#feat=classlist。 - corbacho

13

其他回答已经涉及“jQuery vs. 纯JS”的广泛问题。从你的OP来看,我认为你只是想知道在选择使用jQuery后何时最好使用vanilla JS。你的示例是使用vanilla JS的完美例子:

$(this).attr('id');

这比下面的代码(在我看来)更慢和不易读懂:

this.id.

之所以更慢是因为你需要启动一个新的JS对象才能以jQuery方式检索属性。现在,如果你要使用$(this)执行其他操作,那么请将该jQuery对象存储在变量中并操作。然而,我遇到过许多情况,我只需从元素中获取属性(例如idsrc)。

还有其他类似的常见做法吗?哪些Javascript操作可以更容易地完成,而不必混合使用jQuery。还是这种情况很少见?(即需要更多的代码来实现jQuery的“快捷方式”)

我认为最常见的情况是你在帖子中描述的那种情况;人们不必要地将$(this)包装在jQuery对象中。我最经常看到的是idvalue(而不是使用$(this).val())。

编辑:这里有一篇文章解释了为什么在attr()情况下使用jQuery会更慢。坦白说:我从标签wiki中偷了它,但我认为它值得提到这个问题。

再次编辑: 鉴于直接访问属性会影响可读性/性能,我认为一个好的经验法则可能是尽可能使用 this.<attributename>。可能有一些情况由于浏览器不一致而无法使用,但最好首先尝试这种方法,如果不起作用,则退而求其次使用jQuery。


哈哈,感谢阅读问题 ;) ... 所以这更广泛地是异常情况? - jondavidjohn
@jondavidjohn:老实说,我有些犹豫是否要这样做。由于跨浏览器的问题,我认为使用jQuery通常更有优势。还有其他例子,比如this.style.display = 'none'。这比$(this).hide()更好吗?我认为后者更易读,但前者可能更快。在将元素包装在jQuery对象中执行操作之前,自己进行一些实验以查看某些操作是否可以在没有jQuery的情况下跨浏览器工作是没有坏处的。 - Andrew Whitaker

10

如果你主要关心性能,你的主要示例确实抓住了要点。无谓或多余地调用jQuery,在我看来,是慢速表现的第二大原因(第一大原因是糟糕的DOM遍历)。

这并不是你要寻找的真正示例,但我经常看到这种情况,因此有必要提一下:加快jQuery脚本性能的最佳方法之一是缓存jQuery对象和/或使用链式调用:

// poor
$(this).animate({'opacity':'0'}, function() { $(this).remove(); });

// excellent
var element = $(this);
element.animate({'opacity':'0'}, function() { element.remove(); });

// poor
$('.something').load('url');
$('.something').show();

// excellent
var something = $('#container').children('p.something');
something.load('url').show();

有趣...这种变量实例化和操作分离是否真的会带来处理性能上的提升?还是只是允许操作单独执行(使其不那么受阻碍)? - jondavidjohn
1
它肯定会带来性能提升,但仅当您重复使用所选元素时才会发生。因此,在最后一个例子var something中,我并不真正需要缓存jQuery对象(因为我使用了链接),但出于习惯我还是这样做了。 - Stephen
2
另一方面,以同样的例子为例。调用 $('.something') 两次意味着 jQuery 必须遍历 DOM 两次,寻找所有具有该选择器的元素。 - Stephen
2
在第一个例子中,缓存$(this)可以减少对jQuery函数的调用。这在循环场景中将带来最大的收益。 - Stephen
你的第一个例子是错误的 - 如果原始的.animate()函数的选择器涵盖了多个元素,则动画完成回调将把this设置为单个元素(并且会一遍又一遍地调用)。 - Alnitak
2
@Alnitak 注意到 element 等于 $(this)。它不包含多个元素。 - Stephen

8
我发现JS和JQ之间确实存在重叠。你展示的代码就是一个很好的例子。坦白地说,使用JQ而不是JS最好的原因就是浏览器兼容性。即使我可以用JS完成某些操作,我总是更倾向于使用JQ。

9
如果没有重叠,那就奇怪了,因为JQuery就是JavaScript。 - simon

6

这是我的个人观点,但由于jQuery实际上是JavaScript,理论上它永远不能比原生JS表现更好。

但在实践中,它可能比手写的JS表现更好,因为一个人手写的代码可能不像jQuery那样高效。

最后总结一下——对于较小的项目,我倾向于使用原生JS,对于JS密集型项目,我喜欢使用jQuery而不是重新发明轮子——这也更具生产力。


1
有很多例子表明,库的性能优于纯JS。原来,JS的许多内置原型方法都写得很糟糕。 - Union find

3
< p >第一条答案中 this 作为DOM元素的活动属性列表非常完整。

您可能还会发现其他有趣的内容。

this 是文档时:

  • this.forms 获取当前文档表单的 HTMLCollection
  • this.anchors 获取所有已设置 name 属性的 HTMLAnchorElementHTMLCollection
  • this.links 获取所有已设置 href 属性的 HTMLAnchorElementHTMLCollection
  • this.images 获取所有 HTMLImageElementHTMLCollection
  • 与已弃用的 applets 相同,使用 this.applets

使用 document.forms 时,document.forms [formNameOrId] 获取所命名或标识的表单。

当此为表单时:

  • this [inputNameOrId] 获取所命名或标识的字段

当此为表单字段时:

  • this.type 获取字段类型

在学习jQuery选择器时,我们经常跳过学习已存在的HTML元素属性,这些属性的访问速度非常快。


提到的这些属性在DOM Level 2 HTML规范中被定义,并且得到了广泛的支持。document.embedsdocument.plugins也是广泛支持的(DOM 0)。 document.scripts是一个方便的集合,它在HTML5规范中被定义,并且也得到了广泛的支持(还有Firefox 9+)。 - Rob W
@Robw 所以现在列表可能已经完整了,肯定是有用且快速的。谢谢 ;) - lib3d

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