jQuery 的横空出世,至今已有十个年头了,而它的长盛不衰显然不是没有理由的。jQuery 提供了极为友好的接口,使得开发者们可以方便地进行 DOM 操作、发起 Ajax 请求、生成动画……不一而足。此外,与 DOM API 不同的是,jQuery 采用了 “混合模式”。这意味着你可以在任何一个 jQuery 集合身上调用 jQuery 方法,而不用关心它到底包含了几个元素(不管是零个、一个或多个,都没问题)。
jQuery 3 修复了大量的 bug,增加了新的方法,同时移除了一些接口,并修改了少量接口的行为。
我们先来讨论 jQuery 3 中最重要的几个新增特性。
for...of
Loopfor...of
循环for...of
循环语句来迭代一个 jQuery 集合中的所有 DOM 元素。这种新的迭代方法是 ECMAScript 2015(即 ES6)规范中的一部分。这个方法可以对 “可迭代对象”(比如 Array
、Map
、Set
等)进行循环。 当使用这种新的迭代方法时,你在循环体内每次拿到的值并不是一个 jQuery 对象,而是一个 DOM 元素(译注:这一点跟 .each()
方法类似)。当你在对一个 jQuery 集合进行操作时,这个新的迭代方法可以少许改善你的代码。
为了搞清楚这种迭代方法到底是怎么工作的,我们来假设一个场景——你需要给页面中的每个 input
元素分配一个 ID。在 jQuery 3 之前,你可能会这样写:
var $inputs = $('input');
for(var i = 0; i < $inputs.length; i++) {
$inputs[i].id = 'input-' + i;
}
而在 jQuery 3 中,你就可以这样写了:
var $inputs = $('input');
var i = 0;
for(var input of $inputs) {
input.id = 'input-' + i++;
}
$.get()
和 $.post()
函数的新签名jQuery 3 为 $.get()
和 $.post()
这两个工具函数增加了新签名,从而使得它们和 $.ajax()
的接口风格保持一致。新签名是这样的:
$.get([settings])
$.post([settings])
settings
是一个对象,它包含多个属性。它的格式和你以前传给 $.ajax()
的参数格式是一样的。如果你想更清楚地了解这个参数对象,请参考 $.ajax()
页面 中的相关描述。$.get()
和 $.post()
的参数对象与传给 $.ajax()
的参数相比,唯一的区别就是前者的 method
属性总是会被忽略。原因其实也很简单,$.get()
和 $.post()
本身就已经预设了发起 Ajax 请求的 HTTP 方法了(显然 $.get()
就是 GET,而 $.post()
就是 POST)。也就是说,正常人类应该是不会想用 $.get()
方法来发送一个 POST 请求的。
假设有以下一段代码:
$.get({
url: 'http:',
method: 'POST' // 这个属性将被忽略
});
不管我们把 method
属性写成什么,这个请求总是会以 GET 的方式发出去的。
requestAnimationFrame()
for AnimationsrequestAnimationFrame()
来实现动画所有现代浏览器(包括 IE10 及以上)都是支持 requestAnimationFrame
的。jQuery 3 将会在内部采用这个 API 来实现动画,以便达到更流畅、更省资源的动画效果。
unwrap()
方法jQuery 3 为 unwrap()
方法增加了一个可选的 selector 参数。这个方法的新签名是这样的:
unwrap([selector])
:visible
和 :hidden
jQuery 3 将会修改 :visible
和 :hidden
过滤器的含义。只要元素具有任何布局盒,哪怕宽高为零,也会被认为是 :visible
。举个例子,br
元素和不包含内容的行内元素现在都会被 :visible
这个过滤器选中。
因此,如果你的页面中包含如下的结构:
<div></div> <br />
然后运行以下语句:
console.log($('body :visible').length);
0
;但在 jQuery 3 中,你会得到 2
。data()
方法另一个重要的变化是跟 data()
方法有关的。现在它的行为已经变得跟 Dataset API 规范 一致了。jQuery 3 将会把所有属性键名转换成驼峰形式。我们来详细看一下,以如下元素为例:
<div id="container"></div>
当我们在用 jQuery 3 以前的版本时,如果运行如下代码:
var $elem = $('#container');
$elem.data({
'my-property': 'hello'
});
console.log($elem.data());
{my-property: "hello"}
{myProperty: "hello"}
Deferred
对象Deferred
对象的行为。Deferred
对象可以说是 Promise
对象的前身之一,它实现了对 Promise/A+ 协议 的兼容。这个对象以及它的历史都相当有意思。如果想要深入了解,你可以去阅读 jQuery 官方文档或者
《jQuery 实战(第三版)》——这本书也涵盖了 jQuery 3。在 jQuery 1.x 和 2.x 中,传给 Deferred
的回调函数内如果出现未捕获的异常,会立即中断程序的执行(译注:即静默失败,其实 jQuery 绝大多数回调函数的行为都是这样的)。而原生的 Promise
对象并非如此,它会抛出异常,并不断向上冒泡,直至到达 window.onerror
(通常冒泡的终点是这里)。如果你没有定义一个函数来处理这个错误事件的话(通常我们都不会这么做),那这个异常的信息将会被显示出来,此时程序的执行才会停止。
jQuery 3 将会遵循原生 Promise
对象的模式。因此,回调内产生的异常将会导致失败状态(rejection),并触发失败回调。一旦失败回调执行完毕,整个进程就将继续推进,后续的成功回调将被执行。
为了让你更好地理解这个差异,让我们来看一个小例子。比如我们有如下代码:
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();
window.onerror
定义任何事件处理函数,控制台将会输出 “Uncaught Error: An error”,而且程序的执行将中止。而在 jQuery 3 中,整个行为是完全不同的。你将在控制台中看到 “Failure 1” 和 “Success 2” 两条消息。那个异常将会被第一个失败回调处理,并且,一旦异常得到处理,那么后续的成功回调将被调用。
addClass()
和 hasClass()
这样的方法来操作 SVG 文档了。SVGbind()
、unbind()
、delegate()
和 undelegate()
方法on()
方法,它提供了一个统一的接口,用以取代 bind()
、delegate()
和 live()
等方法。与此同时,jQuery 还引入了 off()
这个方法来取代 unbind()
、undelegated()
和 die()
等方法。从那时起,bind()
、delegate()
、unbind()
和 undelegate()
就已经不再推荐使用了,但它们还是一直存在着。
jQuery 3 终于开始将这些方法标记为 “废弃” 了,并计划在未来的某个版本(很可能是 jQuery 4)中将它们彻底移除。因此,请在你的项目中统一使用 on()
和 off()
方法,这样你就不用担心未来版本的变更了。
load()
、unload()
和 error()
方法load()
、unload()
和 error()
等已经标记为废弃的方法。这些方法在很早以前(从 jQuery 1.8 开始)就已经被标记为废弃了,但一直没有去掉。如果你正在使用的某款插件仍然依赖这些方法,那么升级到 jQuery 3 会把你的代码搞挂。因此,在升级过程中请务必留意。context
、support
和 selector
属性context
、support
和 selector
等已经标记为废弃的属性。同上,在升级到 jQuery 3 时,请留意你正使用的插件。width()
和 height()
的返回值将不再取整width()
、height()
和其它相关方法的一个 bug。这些方法的返回值将不再舍入取整,因为这种取整行为在某些情况下不便于对元素进行定位。我们来详细看一看。假设你一个宽度为 100px
的容器元素,它包含了三个子元素,宽度均为三分之一(即 33.333333%):
<div class="container">
<div>My name</div>
<div>is</div>
<div>Aurelio De Rosa</div>
</div>
在 jQuery 3 以前的版本中,如果你尝试通过以下代码来获取子元素的宽度
$('.container div').width();
33
。原因在于 jQuery 会把 33.33333 这个值取整。而在 jQuery 3 中,这个 bug 已经被修复了,因此你将会得到更加精确的结果(即一个浮点数)。wrapAll()
方法wrapAll()
方法中的一个 bug,这个 bug 出现在把一个函数作为参数传给它的情况下。在 jQuery 3 以前的版本中,当一个函数被传给 wrapAll()
方法时,它会把 jQuery 集合中的每个元素单独包裹起来。换句话说,这种行为和把一个函数传给 wrap()
时的行为是完全一样的。在修复这个问题的同时,还引入了另外一个变更:由于在 jQuery 3 中,这个函数只会调用一次了,那就无法把 jQuery 集合中每个元素都传给它。因此,这个函数的执行上下文(this
)将只能指向当前 jQuery 集合中的第一个元素。
Deferred
对象的改进等等。同样,在升级某个第三方库时,也有必要检查一下该项目的兼容性情况,以便尽早发现任何非预期行为,避免某些功能失效。