Django创始人:从技术工艺上考量jQuery

开发 开发工具 前端
文章的作者Simon Willison来自英国,是一名经验丰富的开发人员。曾工作于Yahoo,是Web开发框架Django的创始人之一,也是OpenID的推动者。这是一篇针对jQuery的描述性文章,提到了jQuery的技术优势,以及应用特点,该文被引入了jquery.com的教程库,对jQuery的推动有一定作用。

jQuery在2006年1月现身时,它给人的***印象是,这玩意儿构造得很精明。基于CSS选择器来打点一切,其思路相当灵巧。但链盒工事看起来更像个噱头,并且整体看来,jQuery库提供的功能并不能覆盖所有基础性的东西,jQuery会不会只是昙花一现呢?

jQuery拥有简洁的方法,把大量常用功能封装起来,并提供精巧的插入式API,来满足标准库之外的功能模块的实现。jQuery秉持的核心,乃DOM元素的集合——它把元素集合作为一个根本,给高度抽象出来了。最重要的,是这种遵循***实践的抽象,能让jQuery与其他JavaScript代码相处融洽。

很多对jQuery的介绍,都是针对设计师和初级开发人员。但接下来文章想说明的是,为什么jQuery也会吸引那些富有经验的开发人员。(相关阅读:jQuery让开发者恋恋不舍的秘密

名称空间

编写可重用的、优秀的JavaScript代码,其关键在于对名称空间的积极把控。JavaScript只拥有单一的、全局的名称空间(即window对象),而很多程序员(以及一些库)恣意地为之添加各种东西。要知道全局变量是魔鬼!聪明的开发人员,会使用类似组件模式的技术,来尽力减少全局对象的数量。

jQuery仅向全局名称空间引入一个标记:jQuery函数/对象。其余的要么是jQuery的直接属性,要么就是调用jQuery函数所返回的对象的方法。

那 “语言升级”又是什么呢?大多数库会提供映射,过滤,剥离,往往是浏览器的JavaScript引擎所缺少的那些功能。还有一些库,直接扩 展了JavaScript内置的String和Array类,但这是冒险的做法。String.prototype和Array.prototype也有各自的名称空间,在其内添加的属性一旦发生冲突,所带来的风险,不亚于在全局环境下的草率大意。

在语言升级方面,jQuery提供了很多函数(功能),但每个函数都被赋给jQuery对象的属性:jQuery.each,jQuery.extend,jQuery.grep,jQuery.map,jQuery.merge以及jQuery.trim。如此一来,它们就不会跟其他代码产生冲突。

声名狼藉的$函数

jQuery是唯一被引入的全局标记,其实并不尽然:$标记作为jQuery的快捷方式,也被引入进来。庆幸的是,$的存在不会带来负面影响:如果你需要让原始的$起死回生(比如,这之前你的代码使用了Prototype),你可以调用jQuery.noConflict()来恢复它。如果你既想拥有$的便利,又不希望jQuery跟其他同样使用了全局$函数的代码发生冲突,可遵循jQuery文档所建议的惯用方式:

  1. (function($) {  
  2. // 在这个函数体里,$可作为jQuery的引用  
  3. // 很方便,对吧?  
  4. })(jQuery);  

把一切都附加到$标记的做法,曾让人觉得jQuery华而不实。不过,从体系的角度来审视这种设计,一切又是非常明了的。

#p#

选取元素

jQuery的每个操作,都以选取DOM中一个或更多的节点作为开始。jQuery(拥有一种真正的面向特定领域)的选取语法,是十分有趣的,它结合了CSS 1,CSS 2,部分CSS 3语法,一些XPath语法,以及一些特定的扩展。几个有用的例子:

  1. jQuery(‘div.panel’)   
  2. 选取了所有class="panel"的div   
  3.  
  4. jQuery(‘p#intro’)   
  5. 选取了所有id="intro"的段落   
  6.  
  7. jQuery(‘div#content a:visible’)   
  8. 选取了id="content"的div中所有可见的链接   
  9.  
  10. jQuery(‘input[@name=email]‘)   
  11. 选取了所有name="email"的输入域   
  12.  
  13. jQuery(‘table.orders tr:odd’)   
  14. 选取了类名为“orders”的表中所有的奇数行   
  15.  
  16. jQuery(‘a[@href^="http://"]‘)   
  17. 选取了所有(以http://开头的)外部链接   
  18.  
  19. jQuery(‘p[a]‘)   
  20. 选取了所有包含一个或多个链接的段落  

上述例子中,:visible和:odd是jQuery实现的扩展,很具特色。而属性的选取使用@作为标记,其方式和XPath一样,要优于CSS 2。jQuery的这套选取语法包罗万象,有些类似正则表达式,想完全消化是需要花上一段时间的。

通过jQuery的选取操作,我们能得到一些很棒的“素材”。它们是一个集合,包含了DOM元素,并且类似数组那样,拥有length属性;通过索引可以访问集合中的元素。在Firebug console的交互模式下,集合也被显示成一个数组,这个特性非常有用。集合实际上是一个jQuery对象,这个对象被赋予了很多方法,用来查询,修改,扩展集合中的元素。

jQuery的方法,本质上可分成三种:一种可以操作那些符合匹配的元素;一种可以返回***个匹配到的对象的值;一种可以变更被选取的集合。这里不会列出所有的方法。如果你的浏览器装了Firebug,你可以以交互方式运行这些示例代码:首先使用这个bookmarklet(译注[1])把jQuery库载入至浏览器的任意页面,然后把示例代码粘贴到Firebug console中。

  1. jQuery(‘div#primary’).width(300);   
  2. id="primary"的div的宽度设为300px   
  3.  
  4. jQuery(‘p’).css(‘line-height’, ‘1.8em’);   
  5. 把所有段落的line-height设为1.8em   
  6.  
  7. jQuery(‘li:odd’).css({color: ‘white’, backgroundColor: ‘black’});   
  8. 向间隔的list项添加两个CSS规则;注意css()函数可以用一个对象来代替两个字符串作为参数  
  9.  
  10. jQuery(‘a[@href^="http://"]‘).addClass(‘external’).attr(‘target’, ‘_blank’);   
  11. 向所有(以http://开头的)外部链接添加“external”类,然后策略性地加上target="_blank"属性。这个示例用到了链盒(chaining),稍后会做介绍。  
  12.  
  13. jQuery(‘blockquote’).each(function(el) { alert(jQuery(this).text()) });   
  14. 遍历页面上的每个<blockquote>,并显示出它的文字内容(包括HTML标签)   
  15.  
  16. jQuery(‘a’).html(‘Click here!’);   
  17. 用阴险的“Click here!”代替页面上所有的链接<a>的文字  

下面的示例展示了jQuery如何取得***个匹配到的对象的值:

  1. var width = jQuery(‘div’).width();   
  2. 页面上***个div的宽度   
  3.  
  4. var src = jQuery(‘img’).attr(’src’);   
  5. 页面上***张图片的src属性值   
  6.  
  7. var color = jQuery(‘h1′).css(‘color’);   
  8. ***个<h1>的颜色样式值 

在jQuery的方法构造中,蕴含着令人惬意的对称性:当向方法传递两个参数或一个对象时,方法可被用来执行设置操作;如果只向方法传递一个参数,则可以让它执行取值操 作(译注:读者可对照上面的示例代码感受一下)。这种对称性设计贯穿了jQuery体系,使得API的文法更容易被记忆。***的例子,展示了一些可变更被选取的元素集合的方法。这些方法大多都提高了检索DOM的简易程度:

  1. jQuery(‘div’).not(‘[@id]‘)   
  2. 返回那些没有id属性的div   
  3.  
  4. jQuery(‘h2′).parent()   
  5. 返回那些是<h2>的直接父节点元素  
  6.  
  7. jQuery(‘blockquote’).children()   
  8. 返回所有<blockquote>的子节点元素  
  9.  
  10. jQuery(‘p’).eq(4).next()   
  11. 在页面上找到第五个段落,然后根据节点的树层结构关系,找到并返回这个段落节点右侧的兄弟节点元素  
  12.  
  13. jQuery(‘input:text:first’).parents(‘form’)   
  14. 找到并返回页面上***个type="text"的输入域所在的form节点元素,parents()的可选参数是另一个选择器 

#p#

链盒

jQuery开发团队经常夸耀jQuery的链盒理念,甚至在网站首页上宣扬“jQuery将改变你编写JavaScript的方式”。个人感觉,这么做多少有点误导大众,这里告诉大家,你完全可以取jQuery之长,却应避免冗长的方法链盒。

也就是说,链盒有时会像变戏法一样。除了使用链盒将各种操作DOM的方法粘到一起,你也可以使用jQuery的end()方法,来实现在特定范围内推进或回 溯你需要得到的元素。这个概念很难解释清楚。本质上讲,每次使用(诸如children()或filter())方法来改变元素集合时,你可以在这些方法 之后使用end(),来重新定位你最初选取的元素集合。

  1. $('form#login')  
  2. // ***步,隐藏表单中那些带有'optional'类的&lt;label&gt; 
  3. .find('label.optional').hide().end()    
  4. // 第二步,为表单的密码输入域渲染上红色边框  
  5. .find('input:password').css('border', '1px solid red').end()  
  6. // 第三步,为表单加上提交处理   
  7. .submit(function(){  
  8. return confirm('Are you sure you want to submit?');  
  9. });  

这个示例读起来就像句俏皮话。整个过程是,先选取一个表单,再在其中选取一些元素做修改,然后回溯到表单,为它定义一个submit()处理。示例很酷,但如果你不习惯,也可以不这么用。

操作DOM

jQuery提供了几个大规模操作DOM的卓越方法。***种非常让人惊叹:jQuery()函数能把HTML片段插入DOM元素中(实际上,函数会留意以’<’打头的字符串参数):

  1. var div = jQuery('&lt;div&gt;Some text&lt;/div&gt;');  

一旦你创建好了div,便可以继续用链盒向其添加属性:

  1. var div = jQuery('&lt;div&gt;Some text&lt;/div&gt;').addClass('inserted').attr('id', 'foo');  

现在把div加到body上:

  1. div.appendTo(document.body)  

或用选择器把div加到已知元素的前面:

  1. div.prependTo('div#primary')  

处理事件

任何JavaScript库都需要事件处理能力,jQuery也不例外。类似attr()和css()的行为,各种与事件处理相关的方法也有双重用途:一种 是把函数当作参数,赋给事件处理器;一种是不带参数,可以模拟事件被触发:

  1. jQuery(‘p’).click(function() { jQuery(this).css(‘background-color’, ‘red’); });   
  2. 为所有段落增加点击事件,当你点击它们时,段落背景会变成红色  
  3.  
  4. jQuery(‘p:first’).click()   
  5. 然后在***个段落上模拟点击的动作,它的背景会变成红色  

类似的函数还包括mouseover,keyup等,对应着浏览器通常支持的那些动作。留意一下事件处理中的’this’关键字,它代表触发事件的元素;jQuery(this)是一种惯用语法,可以让this所代表的元素应用各种jQuery方法。这里有两个与事件相关的函数值得仔细说一下:

  1. jQuery('a').hover(function() {  
  2. jQuery(this).css('background-color', 'orange');  
  3. }, function() {  
  4. jQuery(this).css('background-color', 'white');  
  5. });  

hover()可设定两个函数,分别对应onmouseover和onmouseout事件。

  1. jQuery('p').one('click', function() {   
  2. alert(jQuery(this).html());   
  3. });  

one()设定的事件在***次被触发后便被移除。上面的示例会让所有段落在***次被点击时显示其文字内容。凭借bind()和trigger()方法,jQuery也可以支持自定义事件(click()家族仅仅是便捷方法,只支持有限的事件)。自定义事件可接受参数,trigger()可接受数组作为参数,来做各种处理操作:

  1. jQuery(document).bind('stuffHappened', function(event, msg) {  
  2. alert('stuff happened: ' + msg);  
  3. });  
  4. jQuery(document).trigger('stuffHappened', ['Hello!']);  

#p#

渐进式编码

本人一直认为,***的Web应用程序,往往是那些在脚本被禁用后仍能正常使用的程序。想建立这样的应用程序,***的方法就是遵循渐进式编码,让普通页面完全加载后,再为页面中的元素赋以事件处理。

jQuery对这种编码策略提供了绝好支持。首先,从整体上看,节点选取暗合jQuery以及渐进式编码的核心理念。其次,针对window.onload问题,jQuery提供了一套解决方案,这套方案借鉴了Dean Edward的成果,使得以“DOM加载完毕”为信号的事件能跨浏览器工作。你可以在浏览器完全加载DOM后设定并运行一个函数,如下所示:

  1. jQuery(document).ready(function() {  
  2. alert('The DOM is ready!');  
  3. });  

你甚至可以直接传递一个函数给jQuery(),以更简洁的方式达到同样效果:

  1. jQuery(function() {  
  2. alert('The DOM is ready!');  
  3. });  

jQuery与Ajax

在我所知道的主流JavaScript库中,jQuery拥有最棒的Ajax API。最简单的Ajax调用如:

  1. jQuery('div#intro').load('/some/fragment.html');  

代码以GET请求方式,从/some/fragment.html文件中获取HTML片段,并把片段装载到id="intro"的div中。

***次看到这行代码时,几乎对它没什么印象。这看起来非常简洁,但如果你想用jQuery做些更复杂的事情,比如显示Ajax装载进度,该如何做呢?jQuery为你准备了一些可自定义的事件(ajaxStart,ajaxComplete,ajaxError等等),来实现你想要的代码。同时jQuery也提供了广泛的底层API,来实现更复杂的Ajax交互:

  1. jQuery.get('/some/script.php', {'name': 'Simon'}, function(data) {  
  2. alert('The server said: ' + data);  
  3. });   
  4. // 以GET方式通过/some/script.php?name=Simon获取数据  
  5. jQuery.post('/some/script.php', {'name': 'Simon'}, function(data) {  
  6. alert('The server said: ' + data);  
  7. });   
  8. // 以POST方式向/some/script.php发送请求  
  9. jQuery.getJSON('/some.<a target="_blank" href="http://www.yeeyan.com/articles/tag/js"> 
  10. <em>js</em></a>on', function(json) {  
  11.   alert('JSON rocks: ' + <a target="_blank" href="http://www.yeeyan.com/articles/tag/js"> 
  12. <em>js</em></a>on.foo + ' ' + json.bar);  
  13. });  
  14. // 从/some.<a target="_blank" href="http://www.yeeyan.com/articles/tag/js"> 
  15. <em>js</em></a>on接收并解析数据,把数据转换成JSON格式  
  16. jQuery.getScript('/script.<a target="_blank" href="http://www.yeeyan.com/articles/tag/js"> 
  17. <em>js</em></a>');   
  18. // 以GET方式获取/script.js脚本并用eval()执行  

插件

就你所能获得的功能的数量而言,jQuery库其实是相当小的——对代码做紧凑处理后只有20KB左右,甚至用gzip压缩后会变得更小。向标准库添加额外功能时,需用插件的方式来做,它可以(也确实能够)向现有的jQuery实例对象添加全新的方法。如果你想执行:

  1. jQuery('p').bounceAroundTheScreenAndTurnGreen();  

jQuery的插件机制提供了文档说明型的挂载方式,可以实现把上述方法添加到jQuery中。这种简易的创建形式,吸引了很多插件作者,他们让人印象深刻;现在插件目录中已经有超过100个插件了。真正绝妙的,是你可以像自定义方法那样,来定义选择器。比如,moreSelectors插件实现了诸如“div:color(red)”的方法,来匹配包含红色文本的div。

并非天衣无缝

在发掘jQuery各种特性的同时,我也被某个我视之为教条的东西所折磨着。几年来,我总是建议大家使用一种JavaScript库,前提是你愿意梳理它的源码,并把它的工作原理彻底搞懂。

jQuery使用了相当不可思议的技术,以求实现它所设想的各种功能——其中一些(比如选择器的代码)真是震天骇地。如果有必要彻底搞懂一个库的工作原理,那么对大多数开发人员来说,jQuery不会是上佳之选。然而,jQuery拥有极高的人气,并且没有太多与之相关的恐怖经典流于街巷,所以具体到jQuery所用技艺的邪正之分,也就变得不那么重要了。

这里必须重新审视曾给大家的建议。库的运作机制并不是问题焦点:关键是应看清更具普遍性的潜在问题,知晓浏览器之间的差别,以及你使用库的哪种技术,来消除差别造成的负面影响。没有哪种库可以一劳永逸地帮你克服浏览器的古怪行为。但只要你对应付潜在问题训练有素,便可把握经脉,指出问题的源头——无论它们来自你自己编写的代码,还是库或者应付策略本身。

结语

通过本文,希望能让大家明白,jQuery不只是又一个JavaScript库那么简单——它蕴含了很多值得品味揣摩的理念,甚至能启迪那些骨灰级的JavaScript程序员。如果你不打算尝试jQuery,但仍值得去花些时间探索一下jQuery的生态体系。

原文作者:Simon Willison                yangyang 译

中文链接:http://article.yeeyan.org/view/davidkoree/4568?from_com

【编辑推荐】

  1. jQuery四大天王:核心函数详解
  2. jQuery让开发者恋恋不舍的秘密
  3. jQuery开发者:你真的需要一个插件吗?
  4. 小团队大成功 春风得意的jQuery
  5. jQuery中10个强大的遍历函数
责任编辑:王晓东 来源: cssrainbow
相关推荐

2014-10-23 09:03:40

创业合伙人

2014-10-24 10:17:56

程序员

2014-11-19 11:50:39

OneAPM

2010-05-04 16:22:45

Unix系统

2020-04-01 09:00:56

容器安全操作

2012-06-14 16:21:24

LinuxLinus Torva

2022-12-06 07:18:56

DedeCMS创始人林学

2009-12-09 13:53:32

PostgreSQLMySQL

2013-03-14 09:11:15

AndroidChromeChrome OS

2009-05-20 13:40:22

GoogleTwitter即时搜索

2012-04-02 19:17:37

苹果

2016-06-16 17:13:59

Hadoop

2013-07-29 09:16:33

2013-04-08 10:38:00

2009-06-19 16:44:56

SpringJava EE

2015-09-07 13:46:29

技术联合创始人

2009-03-05 09:53:03

酷六钟胜辉离职

2011-08-23 17:34:11

雨林木风Linux 20周年

2010-10-11 10:21:49

Flipboard移动开发

2010-08-20 09:27:40

Java创始人Android
点赞
收藏

51CTO技术栈公众号