这博客上已经有很多关于 jQuery 和 Prototype 代码大小差异的帖子。这些帖子的基本前提(我同意)是,由于 jQuery 代码的结构方式,与 Prototype 相比,框架中所有典型的 Javascript 设计模式都变得更短更简单。
长期以来,我一直是一个自认的 Prototype 爱好者。当我开始做 Rails 工作时,我发现了这个框架,对于 Rails 开发者来说,几乎没有其他选择。Prototype,以其丑陋的姿态,被烘焙到 Rails 中,用手工编写 JavaScript 来放弃 Rails 提供的生产力提升是非常困难的。进一步说,这就是为什么我开始着手开发 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 插件
- 自动循环。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 将其作为一项基本设计决策的方式。