本博客上已经发表了很多关于 jQuery 和 Prototype 之间代码大小差异的文章。这些文章的基本前提(我同意)是,由于 jQuery 代码的结构方式,与 Prototype 相比,框架中所有典型的 Javascript 设计模式都变得更短、更简单。
在很长一段时间里,我承认自己是一个 Prototype 爱好者。当我开始做 Rails 工作时,我发现了这个框架,对于 Rails 开发人员来说,几乎没有其他选择。Prototype 以其所有丑陋之处融入了 Rails,并且很难放弃 Rails 通过手动编写 Javascript 代码带来的生产力提升。更进一步地说,这就是我开始着手开发 jQuery on Rails 的原因,它的目标是允许 Rails 开发人员将 jQuery 作为 Prototype 的直接替换。
但回到这篇文章的目的,jQuery 的编程理念与 Prototype 的编程理念在根本上有所不同,不仅仅是代码大小。事实上,这种理念上的差异与 Java 和 Ruby 之间的理念差异非常相似,因此 Rails 社区如此全面地拥抱 Prototype 颇具讽刺意味。
让我们看看一些代码比较。首先,在特定节点之后添加一些任意 HTML。
在 Prototype 中
new Insertion.After('myId', 'Arbitrary HTML');
在 jQuery 中
$('#myId').after('Arbitrary HTML');
现在,我们还没有在减少代码冗余方面做太多工作(尽管 jQuery 的代码更简洁),但在 Prototype 和 jQuery 处理问题的方式上存在根本差异。
Prototype 创建了一系列单片类,每个类都封装了一些功能。然后,开发人员传入一个 ID 和其他一些参数,类就会执行它应该执行的操作。非常类似于 Java 封装功能的方式(例如 Math 类)。**注意:**这并不是说 Java 不能以这种方式做事情。
jQuery 以一种根本不同的方式处理问题。它将 HTML 节点集合视为要向其传递消息的对象(更像是传统的 Ruby 方式)。因此,它没有使用单独的类来在 HTML 节点之后添加文本,而是将功能粘贴到 jQuery 对象上,该对象由 $ 函数返回。相比之下,Prototype 的 $ 函数返回一个普通的 DOM 节点。
Prototype 尝试在其最新 RC 版本中添加的 $$ 方法来实现类似的功能,但存在根本差异。虽然 Prototype 的 $$ 返回一个 DOM 元素数组,但 jQuery 的 $ 是整个框架的基础。几乎所有 jQuery 函数都绑定到 jQuery 对象,该对象由 $ 方法返回。
jQuery 方式的优势非常明显
- 可链式调用。 由于 jQuery 对象具有粘贴在其上的功能,因此它们会返回其他 jQuery 对象,开发人员可以将其他消息传递给这些对象。jQuery 网站上的一个简单示例是 $(“p.surprise”).addClass(“ohmy”).show(“slow”);
- 使用 CSS 选择器和 XPath 运算符。 由于 jQuery 将消息传递给对象,因此它可以(并且已经)在 $ 方法中实现额外的选择器功能。粘贴到 jQuery 对象上的方法只看到一个类似数组的对象,其中包含一系列 DOM 元素。它不关心我们是如何获取它们的。因此,插件开发人员可以轻松地将其他解析器添加到 $ 方法中,或将其他函数粘贴到 jQuery 对象中。
- 这带我们来到了插件开发。 jQuery 方式非常有利于插件开发。添加利用 jQuery 对象的功能非常容易,并且 jQuery 插件通常比其对应的插件短得多。 jQuery 插件
- 自动循环。 jQuery 方法需要自动循环遍历数组中的所有 DOM 元素,并应用所需的方法。因此 $(expression).after(‘some HTML’) 透明地将 HTML 添加到表达式返回的每个元素之后。例如,$(‘p’).after(‘some HTML’) 将在页面上的每个 <p> 之后添加 ‘some HTML’。就我个人而言,我认为在大多数情况下消除代码中的迭代(在大多数情况下)是使用 jQuery 的最实用、最日常的优势之一。
- 建立在自身之上。 随着 jQuery 的成熟,在现有架构之上构建插件变得越来越容易。由于所有 jQuery 函数都自动循环,因此使用现有的 jQuery 函数意味着令人讨厌的迭代几乎消失了。
还有更多,但贯穿所有优势的主线来自于 John Resig 非常细致的方式,他让 jQuery 对象/数组接受传递的消息,而不是构建各种单片功能块,这些块中的每一个都必须从头开始构建。
其他一些例子
Prototype 中的 AJAX 更新器
new Ajax.Updater('placeholder', url, { method: 'get', parameters: par });
jQuery 中的 AJAX 更新器
$('#placeholder').load(url + par);
注意:此示例不处理我们在想要将响应加载到每个 <p> 对象中时获得的明显迭代优势,例如。
在 Prototype 中为元素添加类
Element.addClassName('element', 'className');
在 jQuery 中为元素添加类
$('#element').addClass('className');
在 Prototype 中为一组元素添加类
$$('.element').each(function(node) {
Element.addClassName(node, 'className');
}
在 jQuery 中为一组元素添加类
$('.element').addClass('className');
最后一个是最清楚地体现了方法学差异的例子。由于 jQuery 将消息传递给 jQuery 对象,因此代码几乎没有改变。jQuery 不关心我们现在是为一组对象还是一个对象添加类;底层代码是相同的(将类添加到对象中的元素集中)。另一方面,Prototype 需要迭代器。
随着代码变得越来越复杂,jQuery 轻松地扩展,而嵌套循环在 Prototype 这样的框架中成为常态。
[更新] 一位敏锐的读者(Mislav)指出,Prototype 确实做了一些 jQuery 所做的事情。Prototype 似乎将 Element 类绑定到 DOM 元素,允许 $(‘myElement’).hide() 这样的操作,等等。但是,它只适用于 Element 模块,并且似乎只适用于单个 DOM 元素。绑定 Elements 模块很酷,但这更多的是一个事后想法,而不是 jQuery 将其作为一项基本设计决策的方式。