jQuery 的横空出世,至今已有十个年头了,而它的长盛不衰显然不是没有理由的。jQuery 提供了极为友好的接口,使得开发者们可以方便地进行 DOM 操作、发起 Ajax 请求、生成动画……不一而足。此外,与 DOM API 不同的是,jQuery 采用了 “混合模式”。这意味着你可以在任何一个 jQuery 集合身上调用 jQuery 方法,而不用关心它到底包含了几个元素(不管是零个、一个或多个,都没问题)。In a few weeks, jQuery will reach an important milestone with the release of version 3. jQuery 3 fixes a lot of bugs, adds new methods, deprecates and removes some functions, and changes the behavior of a few functions. In this article, I’m going to highlight the most important changes introduced by jQuery 3.
在未来的几周内,jQuery 就将抵达一个重要的里程碑——正式发布 3.0 版本。jQuery 3 修复了大量的 bug,增加了新的方法,同时移除了一些接口,并修改了少量接口的行为。在这篇文章中,我将为大家重点讲解 jQuery 3 所引入的那些最重要的变化。
New Features
新增特性
In the following sections I’ll discuss the most important features added in jQuery 3.
我们先来讨论 jQuery 3 中最重要的几个新增特性。
for…of Loop
for…of 循环
jQuery 3 will offer the possibility to iterate over the DOM elements of a jQuery collection using the for…of loop. This new iterator is part of the ECMAScript 2015 (a.k.a. ECMAScript 6) specification. It lets you loop over iterable objects (including Array, Map, Set, and so on).
在 jQuery 3 中,我们可以用 for…of 循环语句来迭代一个 jQuery 集合中的所有 DOM 元素。这种新的迭代方法是 ECMAScript 2015(即 ES6)规范中的一部分。这个方法可以对 “可迭代对象”(比如 Array、Map、Set 等)进行循环。
When using this new iterator, the value you receive is not a jQuery collection from which you can access one element at a time but rather a DOM element. This iterator will slightly improve the way you can perform operations over a jQuery collection.
当使用这种新的迭代方法时,你在循环体内每次拿到的值并不是一个 jQuery 对象,而是一个 DOM 元素(译注:这一点跟 .each() 方法类似)。当你在对一个 jQuery 集合进行操作时,这个新的迭代方法可以少许改善你的代码。
To understand how this iterator works, let’s say that you want to assign an ID to each input element of a page. Before jQuery 3 you would write:
为了搞清楚这种迭代方法到底是怎么工作的,我们来假设一个场景——你需要给页面中的每个 input 元素分配一个 ID。在 jQuery 3 之前,你可能会这样写:
var $inputs = $('input'); for(var i = 0; i $inputs.length; i++) { $inputs[i].id = 'input-' + i; }
In jQuery 3 you can write:
而在 jQuery 3 中,你就可以这样写了:
var $inputs = $('input'); var i = 0; for(var input of $inputs) { input.id = 'input-' + i++; }
(译注:其实 jQuery 自己是有个 .each() 方法的,可读性也不赖。)
New signature for $.get() and $.post()
$.get() 和 $.post() 函数的新签名
jQuery 3 adds a new signature for the $.get() and the $.post() utility functions to align them to $.ajax(). The new signatures added are:
jQuery 3 为 $.get() 和 $.post() 这两个工具函数增加了新签名,从而使得它们和 $.ajax() 的接口风格保持一致。新签名是这样的:
$.get([settings]) $.post([settings])
settings is an object that can possess many properties. It’s the same object that you can provide to $.ajax(). To learn more about it, refer to the description included in the $.ajax() page.
settings 是一个对象,它包含多个属性。它的格式和你以前传给 $.ajax() 的参数格式是一样的。如果你想更清楚地了解这个参数对象,请参考 $.ajax() 页面 中的相关描述。
The only difference when passing the object to $.get() and $.post() as opposed to$.ajax() is that the method property is always ignored. The reason is that $.get() and $.post() have a preset HTTP method to perform the Ajax request (GET for $.get() and POST for $.post()). Basically, you can’t try to send a POST request via $.get().
$.get() 和 $.post() 的参数对象与传给 $.ajax() 的参数相比,唯一的区别就是前者的 method 属性总是会被忽略。原因其实也很简单,$.get() 和 $.post() 本身就已经预设了发起 Ajax 请求的 HTTP 方法了(显然 $.get() 就是 GET,而 $.post() 就是 POST)。也就是说,正常人类应该是不会想用 $.get() 方法来发送一个 POST 请求的。
Consider the following code:
假设有以下一段代码:
$.get({ url: 'https://www.audero.it', method: 'POST' // This property is ignored // 这个属性将被忽略 });
Despite the method property, this statement won’t send a POST request but a GET request.
不管我们把 method 属性写成什么,这个请求总是会以 GET 的方式发出去的。
Use of requestAnimationFrame() for Animations
采用 requestAnimationFrame() 来实现动画
All modern browsers, including Internet Explorer 10 and above, support requestAnimationFrame. Behind the scenes, jQuery 3 will use this API when performing animations, with the goal of having smoother and less CPU-intensive animations.
所有现代浏览器(包括 IE10 及以上)都是支持 requestAnimationFrame 的。jQuery 3 将会在内部采用这个 API 来实现动画,以便达到更流畅、更省资源的动画效果。
unwrap()
unwrap() 方法
jQuery 3 adds an optional selector parameter to unwrap(). The new signature of this method is:
jQuery 3 为 unwrap() 方法增加了一个可选的 selector 参数。这个方法的新签名是这样的:
unwrap([selector])
有了这个特性,你就可以给这个方法传入一个包含选择符表达式的字符串,用它来在父元素内进行匹配。如果存在匹配的子元素,则这个子元素的父层将被解除;如果没有匹配,则不进行操作。
Features Changed
有变更的特性
jQuery 3 also modifies the behavior of some of its features.
jQuery 3 还修改了一些特性的行为。
:visible and :hidden
:visible 和 :hidden
The new version of jQuery modifies the meaning of the :visible and :hidden filters. Elements will be considered :visible if they have any layout boxes, including those of zero width and/or height. For example, br elements and inline elements with no content will now be selected by the :visible filter.
jQuery 3 将会修改 :visible 和 :hidden 过滤器的含义。只要元素具有任何布局盒,哪怕宽高为零,也会被认为是 :visible。举个例子,br 元素和不包含内容的行内元素现在都会被 :visible 这个过滤器选中。
So, if you have the following mark up in a page:
因此,如果你的页面中包含如下的结构:
<div><div> <br />
然后运行以下语句:
console.log($('body :visible').length);
In jQuery 1.x and 2.x you’ll obtain 0 as a result but in jQuery 3 you’ll obtain 2.
在 jQuery 1.x 和 2.x 中,你得到的结果会是 0;但在 jQuery 3 中,你会得到 2。
data()
data() 方法
Another important change is related to the behavior of the data() method. It has been changed to align the method to the Dataset API specifications. jQuery 3 will transform all the properties’ keys to be camel case. To understand this change, consider the following element:
另一个重要的变化是跟 data() 方法有关的。现在它的行为已经变得跟 Dataset API 规范 一致了。jQuery 3 将会把所有属性键名转换成驼峰形式。我们来详细看一下,以如下元素为例:
<div id="container"><div>
If you’re using a version of jQuery prior to 3 and you write:
当我们在用 jQuery 3 以前的版本时,如果运行如下代码:
var $elem = $('#container'); $elem.data({ 'my-property': 'hello' }); console.log($elem.data());
You’ll obtain the following result on the console:
将会在控制台得到如下结果:
{my-property: "hello"}
In jQuery 3, you’ll obtain this result instead:
而在 jQuery 3 中,我们将会得到如下结果:
{myProperty: "hello"}
Note how in jQuery 3 the name of the property is in camel-case with no dash while in the previous versions it remained lowercase and retained the dash.
请注意,在 jQuery 3 中,属性名已经变成了驼峰形式,横杠已经被去除了;而在以前的版本中,属性名会保持全小写,并原样保留横杠。
The Deferred Object
Deferred 对象
jQuery 3 changes the behavior of the Deferred object, a precursor of the Promise object, to improve its compatibility with the Promise/A+ proposal. This object and its history are quite interesting. To know more about it, you can either read the official documentation or read my book jQuery in Action, third edition that covers jQuery 3 as well.
jQuery 3 还改变了 Deferred 对象的行为。Deferred 对象可以说是 Promise 对象的前身之一,它实现了对 Promise/A+ 协议 的兼容。这个对象以及它的历史都相当有意思。如果想要深入了解,你可以去阅读 jQuery 官方文档,也可以去看我写的书《jQuery 实战(第三版)》——这本书也涵盖了 jQuery 3。
In jQuery 1.x and 2.x, an uncaught exception inside a callback function passed to a Deferred halts the program’s execution. Unlike the native Promise object, the thrown exception bubbles up until it (usually) reaches window.onerror. If you haven’t defined a function for this event (which is uncommon), the exception’s message is displayed and the program’s execution is aborted.
在 jQuery 1.x 和 2.x 中,传给 Deferred 的回调函数内如果出现未捕获的异常,会立即中断程序的执行(译注:即静默失败,其实 jQuery 绝大多数回调函数的行为都是这样的)。而原生的 Promise 对象并非如此,它会抛出异常,并不断向上冒泡,直至到达 window.onerror(通常冒泡的终点是这里)。如果你没有定义一个函数来处理这个错误事件的话(通常我们都不会这么做),那这个异常的信息将会被显示出来,此时程序的执行才会停止。
jQuery 3 follows the same pattern followed by the native Promise object. Therefore, a thrown exception is translated into a rejection and the failure callback is executed. Once done, the process continues and the subsequent success callbacks are executed.
jQuery 3 将会遵循原生 Promise 对象的模式。因此,回调内产生的异常将会导致失败状态(rejection),并触发失败回调。一旦失败回调执行完毕,整个进程就将继续推进,后续的成功回调将被执行。
To help you understand this difference, let’s see a small example. Consider the following code:
为了让你更好地理解这个差异,让我们来看一个小例子。比如我们有如下代码:
var deferred = $.Deferred(); deferred .then(function() { throw new Error('An error'); }) .then( function() { console.log('Success 1'); }, function() { console.log('Failure 1'); } ) .then( function() { console.log('Success 2'); }, function() { console.log('Failure 2'); } ); deferred.resolve();
在 jQuery 1.x 和 2.x 中,只有第一个函数(也就是抛出错误的那个函数)会被执行到。此外,由于我们没有为 window.onerror 定义任何事件处理函数,控制台将会输出 “Uncaught Error: An error”,而且程序的执行将中止。
In jQuery 3, the behavior is completely different. You’ll see “Failure 1” and “Success 2” on the console messages. The exception is managed by the first failure function and, once it has been managed, the following success functions are executed.
而在 jQuery 3 中,整个行为是完全不同的。你将在控制台中看到 “Failure 1” 和 “Success 2” 两条消息。那个异常将会被第一个失败回调处理,并且,一旦异常得到处理,那么后续的成功回调将被调用。
SVG Documents
SVG 文档
No jQuery versions, including jQuery 3, officially support SVG documents. However, the truth is that many methods will just work and others, such as those to manipulate class names, have been updated in jQuery 3 to do so. Therefore, in this upcoming version of jQuery you’ll be able to employ methods such as addClass() and hasClass() with your SVG documents and expect everything to work properly.
没有哪一个 jQuery 版本(包括 jQuery 3)曾官方宣称支持 SVG 文档。不过事实上有很多方法是可以奏效的,此外还有一些方法在以前是不行的(比如操作类名的那些方法),但它们在 jQuery 3 中也得到了更新。因此,在 jQuery 3 中,你应该可以放心使用诸如 addClass() 和 hasClass() 这样的方法来操作 SVG 文档了。
Methods and Properties Deprecated or Removed
已废弃、已移除的方法和属性
In addition to the improvements described so far, jQuery also removes and deprecates a few of its features.
在增加了上述改进的同时,jQuery 也移除、废弃了一些特性。
Deprecation of bind(), unbind(), delegate() and undelegate()
废弃 bind()、unbind()、delegate() 和 undelegate() 方法
The on() method was introduced a while ago to provide a unified interface to replace jQuery’s bind(), delegate(), and live() methods. At the same time, jQuery also introduced the off() method to provide a unified interface to replace unbind(), undelegated(), and die(). The use of bind(), delegate(), unbind(), and undelegate() has been discouraged since then and no further action was taken.
jQuery 在很久以前就引入了 on() 方法,它提供了一个统一的接口,用以取代 bind()、delegate() 和 live() 等方法。与此同时,jQuery 还引入了 off() 这个方法来取代 unbind()、undelegated() 和 die() 等方法。从那时起,bind()、delegate()、unbind() 和 undelegate() 就已经不再推荐使用了,但它们还是一直存在着。
jQuery 3 deprecates all of these methods with the intention of removing them in a future version (possibly jQuery 4). Stick with on() and off() methods for all your projects, so you don’t have to worry about future releases.
jQuery 3 终于开始将这些方法标记为 “废弃” 了,并计划在未来的某个版本(很可能是 jQuery 4)中将它们彻底移除。因此,请在你的项目中统一使用 on() 和 off() 方法,这样你就不用担心未来版本的变更了。
Removal of the load(), unload() and error() Methods
移除 load()、unload() 和 error() 方法
jQuery 3 gets rid of the already deprecated load(), unload(), and error() shortcut methods. These methods were deprecated a long time ago (since jQuery 1.8) but they were still around. If you’re employing a plugin that relies on one or more of these methods, upgrading to jQuery 3 will break your code. Hence, pay attention during the upgrade.
jQuery 3 彻底抛弃了 load()、unload() 和 error() 等已经标记为废弃的方法。这些方法在很早以前(从 jQuery 1.8 开始)就已经被标记为废弃了,但一直没有去掉。如果你正在使用的某款插件仍然依赖这些方法,那么升级到 jQuery 3 会把你的代码搞挂。因此,在升级过程中请务必留意。
Removal of context, support and selector
移除 context、support 和 selector 属性
jQuery 3 gets rid of the already deprecated context, support, and selector properties. As I mentioned in the previous section, if you’re still using them in your project or you’re employing a plugin that relies on one or more of them, upgrading to jQuery 3 will break your code.
jQuery 3 彻底抛弃了 context、support 和 selector 等已经标记为废弃的属性。同上,在升级到 jQuery 3 时,请留意你正使用的插件。
Bugs fixed
已修复的 Bug
jQuery 3 fixes a few important bugs of previous versions. In the following section, I’m going to cover two that will make a huge difference in the way you work.
jQuery 3 修复了以往版本中的一些非常重要的 bug。在本节中,我将着重介绍其中两处,因为这两者应该会对你写代码的习惯带来显著影响。
No More Rounding for width() and height()
width() 和 height() 的返回值将不再取整
jQuery 3 fixes a bug of width(), height() and all the other related methods. These methods will no longer round to the nearest pixel, which made it hard to position elements in some situations.
jQuery 3 修复了 width()、height() 和其它相关方法的一个 bug。这些方法的返回值将不再舍入取整,因为这种取整行为在某些情况下不便于对元素进行定位。
To understand the problem, let’s say that you have three elements with a width of a third (i.e. 33.333333%) inside of a container element that has a width of 100px:
我们来详细看一看。假设你一个宽度为 100px 的容器元素,它包含了三个子元素,宽度均为三分之一(即 33.333333%):
<div class="container"> <div>My name<div> <div>is<div> <div>Aurelio De Rosa<div> <div>
Prior to jQuery 3, if you tried to retrieve the width of the children elements as follows…
在 jQuery 3 以前的版本中,如果你尝试通过以下代码来获取子元素的宽度……
$('.container div').width();
…you’d obtain the value 33 as the result. The reason is that jQuery would round the value 33.33333. In jQuery 3, this bug has been fixed, so you’ll obtain more accurate results (i.e. a floating number).
……那么你得到结果将是 33。原因在于 jQuery 会把 33.33333 这个值取整。而在 jQuery 3 中,这个 bug 已经被修复了,因此你将会得到更加精确的结果(即一个浮点数)。
wrapAll()
wrapAll() 方法
The new version of jQuery fixes a bug in the wrapAll() method that occurred when passing a function to it. Prior to jQuery 3, when passing a function to wrapAll(), it wrapped the elements of the jQuery collection individually. Stated in other words, its behavior was the same as passing a function to wrap().
jQuery 3 还修复了 wrapAll() 方法中的一个 bug,这个 bug 出现在把一个函数作为参数传给它的情况下。在 jQuery 3 以前的版本中,当一个函数被传给 wrapAll() 方法时,它会把 jQuery 集合中的每个元素单独包裹起来。换句话说,这种行为和把一个函数传给 wrap() 时的行为是完全一样的。
In addition to fixing this issue, since the function is called only once in jQuery 3, it isn’t passed the index of the element within the jQuery collection. Finally, the function context (this) will refer to the first element in the jQuery collection.
在修复这个问题的同时,还引入了另外一个变更:由于在 jQuery 3 中,这个函数只会调用一次了,那就无法把 jQuery 集合中每个元素都传给它。因此,这个函数的执行上下文(this)将只能指向当前 jQuery 集合中的第一个元素。
Downloading jQuery 3 beta 1
如何下载 jQuery 3 beta 1
If this article was of any interest to you, you might want to try the first beta of jQuery 3. You can obtain it by accessing one of the two URLs below.
既然你已经读到了这里,那说明你很可能想试试 jQuery 3 的第一个 beta 测试版。你可以通过以下两个地址来获取这个版本:
- Non-minified version: https://code.jquery.com/jquery-3.0.0-beta1.js
- Minified version: https://code.jquery.com/jquery-3.0.0-beta1.min.js
- 未压缩版: https://code.jquery.com/jquery-3.0.0-beta1.js
- 压缩版: https://code.jquery.com/jquery-3.0.0-beta1.min.js
It’s also available on npm and you can download it by running the command:
当然,你还可以通过 npm 来下载:
npm install jquery@3.0.0-beta1
Conclusion
结论
Many people state that jQuery is dead and it doesn’t have a place in modern web development anymore. However, its development continues and statistics of its adoption (78.5% in the top million) contradict these claims.
很多人一直在唱衰 jQuery,说它在现代网页开发中已经没有一席之地了。但不管怎样,jQuery 的开发仍在继续,客观的统计数据(在排名前一百万名的网站中占有率高达 78.5%)也让这些论调不攻自破。
In this article, I’ve walked you through the main changes that jQuery 3 will feature. As you might have noticed, this version is unlikely to break any of your existing projects as it doesn’t introduce many breaking changes. Nonetheless, there are some points to keep in mind when upgrading such as the improvement of the Deferred object. As is always the case before update a third-party dependency, a review of the project will help you spot any unexpected behavior or broken functionality.
在本文中,我已经带你了解了一遍 jQuery 3 将会带来的一些重大变化。或许你已经察觉到了,这个版本并不太可能搞挂你的既有项目,因为它引入的破坏性变更其实寥寥无几。不过,在升级到 jQuery 3 的过程中,你还是有必要牢记一些关键点,比如 Deferred 对象的改进等等。同样,在升级某个第三方库时,也有必要检查一下该项目的兼容性情况,以便尽早发现任何非预期行为,避免某些功能失效。
译注
除了本文所提及的变更之外,jQuery 3.0 最大的变化就是彻底放弃对 IE8 的支持。jQuery 团队做出这个决定的原因在于,微软已经在今年年初宣布停止对 IE 8~10 的支持。因此,jQuery 在 3.0 alpha 阶段所发布的 jQuery Compat 项目也就没有继续存在的必要了。
不过,由于 IE8 仍然是中国大陆最流行的浏览器之一,对国内的开发者来说,在短期(甚至中期)内还不得不停留在 jQuery 1.x 版本。
好吧,最后还是说个好消息吧。为帮助用户平滑升级,此次 jQuery 同样会为 3.0 版本提供迁移插件(jQuery Migrate plugin)。在把 jQuery 升级到 3.0 之后同时运行这个插件,即可确保基于 jQuery 1.x 或 2.x 的既有业务代码正常运行;同时,它还将在控制台向你报告既有代码与 jQuery 3 不兼容的地方。当你修复了这些不兼容问题之后,就可以安全地移除这个插件了。