关于 jQuery 和 Prototype 之间代码大小的帖子有很多。这些帖子的基本前提(我同意)是,由于 jQuery 代码的结构,许多典型的 JavaScript 设计模式在框架中比 Prototype 更加简洁明了。
很长一段时间以来,我一直是一个 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 将功能粘合到 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 方法需要自动循环遍历数组中的所有 DOM 元素,并应用所需的方法。因此,$(expression).after(‘some HTML’) 会透明地在表达式返回的每个元素之后添加 HTML。例如,$(‘p’).after(‘some HTML’) 会在页面上的每个 <p> 之后添加 ‘some HTML’。
- 建立在自身之上。 随着 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 将其作为基本设计决策所做的。