加载jQuery-Loading jQuery

1.坚持使用CDN来加载jQuery,这种别人服务器免费帮你托管文件的便宜干嘛不占呢。点击查看使用CDN的好处,点此查看一些主流的jQuery CDN地址。

<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="js/jquery-1.11.0.min.js" type="text/javascript"><\/script>')</script>

下面一行代码是检测jQuery这个全局对象是否存在,存在则已经加载完成,不存在则加载本地的jQuery库(保证下载不到的用户能获取到)

2.安全起见,最好还是提供一个本地备份以便在无法从远程CDN服务器获取jQuery时网站也能工作,如上面代码所示。详情见此。

3.使用裸协议的URL(也就是说去掉http:或者https:),如上面代码展示的那样。(很多网站都转向HTTPS协议了,为了保证不出错最好不写协议,让转发的自己判断)

4.如果可能,尽量将你的JavaScript和jQuery代码放到页面底部。详情移步这里,或者查看一个HTML5页面标准模板。

5.该使用哪个版本?

  • 如果你想兼容IE6/7/8请别用2.x的版本
  • 针对极少数不用考虑兼容性的幸运儿,极力推荐使用最新版本的jQuery
  • 当从CDN服务器加载jQuery时,最好把版本写全(比如1.11.0而不是1.11或者直接写个1)
  • 不要使用从 jQuery CDN处下载的最新的 jquey-latest.js(最新版jQuery)

6.如果你同时还使用了其他JS框架诸如Prototype, MooTools, Zepto云云,因为他们也使用了$符号,所以你就不要再用美刀符号来进行jQuery 编码了,而请用'jQuery'代替。并且调用 $.noConflict() 保证不会有冲突出现(还可以选择是否让出 jQuery 全局对象)。

7.要检测浏览器对一些新特性是否支持,请用Modernizr(一个检测CSS3和HTML5特性的JS脚本,会在body处添加一些检测的结果)。插播广告:论为毛不检测浏览器

关于变量-jQuery Variables

1.jQuery类型的变量最好加个$前缀。

2.时常将jQuery选择器返回的内容存进变量以便重用(这里涉及到性能的问题,一个jQuery数组对象后面其实拥有大量的变量属性,大量使用非常耗费系统资源,特别对浏览器来说,浏览器的内存资源非常宝贵,这里用到了缓存的概念)

var $products = $("div.products"); // 慢
var $products = $(".products"); // 快

3.使用驼峰命名(这里说的是小驼峰命名如 myDiv 这样的)

关于选择器-Selectors

1.尽量ID选择器。其背后机理其实是调用原生的 document.getElementById(),jQuery的ID选择器是调用原生的这个方法,其他的则用到了document.querySelectorAll()方法,老式浏览器还要用Sizzle选择器引擎模拟编译过程匹配一大遍代码才可以模拟实现document.querySelectorAll的类似功能,非常耗费内存资源。

2.使用类选择器时不要指定元素的类型。不信你看这个性能比较

这里要理解Sizzle选择器引擎的原理,类选择器会优先检测document.getElementsByClassName()方法,如果没有的话会使用document.getElementsByTagName()原生方法模拟实现(也是很耗费内存的)。

var $products = $("div.products"); // 慢
var $products = $(".products"); // 快

3.使用find()方法对id->child进行选择,.find()方法更快因为第一个选择器处理没有进过Sizzle选择器引擎。

因为 $  ('#id') 这样的选择器会用document.getElementById() 方法,可以减少Sizzle选择器引擎的分词压力(选择器越多分词的过程就越繁琐,运行的时间就会更久)和减少闭包的函数数量(会将函数构成闭包链驻留在内存中以便后面调用,jQuery牺牲了部分空间换取了执行速度快的目的)。

总的来说就一句话,将用ID选择器和用其他选择器的查找分离。

// 不好的做法,对Sizzle选择器引擎用了嵌套的查询
var $productsIds = $("#products div.id");
// 好的做法,#product已经通过document.getElementById()获取到,只有div.id需要通过Sizzle选择器引擎获取
var $productsIds = $("#products").find("div.id");

4.多级查找中,右边的特殊性尽量指定得多点而左边的特殊性则尽量少点。了解更多

这里这样写的原因是因为Sizzle选择器引擎分词构成函数闭包链之后是采用反向调用的原理从选择器的右边开始调用之前匹配好的函数。选择器越多需要调用的函数则越多,浪费的内存越多和运行时间越长。

// 不好
$("div.data .gonzalez");
// 优化后
$(".data td.gonzalez");

所以应该要记得写选择器代码的时候的原则:越少选择器越好,能获取到你要的元素就可以。而且详细的选择器应该放到右边。

5.避免冗余,选择器越少越好,以能获取到你想要的范围为准。详情或者查看性能比较

$(".data table.attendees td.gonzalez"); 
// 好的方式:去掉了中间的冗余
$(".data td.gonzalez");

6.指定选择器的上下文 ( jQuery是有一个context参数可以缩小选择范围的,默认为document )。

// 劣质的代码:因为需要遍历整个DOM来找到.class
$('.class');
// 高品代码:因为只需在指定容器范围内进行查找
$('.class', '#class-container');

7.不要使用万能选择器,万能选择器会匹配非常多的TAG。查看具体阐释

$('div.container > *'); // 差
$('div.container').children(); // 棒

8.警惕隐式的万能选择器。省略的情况下其实使用的就是*号通配符。更多信息

$('div.someclass :radio'); // 差
$('div.someclass input:radio'); // 棒

9.ID已经表示唯一了,背后使用的是document.getElementById(),所以不要跟其他选择器混搭了。

$('#outer #inner'); // 脏
$('div#inner'); // 乱
$('.outer-container #inner'); // 差
$('#inner'); // 干净利落,后台只需调用document.getElementById()

DOM操作相关-DOM Manipulation

1.操作任何元素前先将其从文档卸载,完了再贴回去。这里减少了访问DOM元素的次数有利于提高性能。这事儿还能说细点

var $myList = $("#list-container > ul").detach();
//...一大堆对$myList的处理
$myList.appendTo("#list-container");

2.使用字符串连接(指+)或者array.join()方法而不是.append()方法。这里也是因为.append()方法用到了DOM中的appendChild方法的原因。所以性能不太好。具体来说,性能比较

// 不好
var $myList = $("#list");
for(var i = 0; i < 10000; i++){
    $myList.append("<li>"+i+"</li>");
}
 
// 比较好
var $myList = $("#list");
var list = "";
for(var i = 0; i < 10000; i++){
    list += "<li>"+i+"</li>";
}
$myList.html(list);
 
// 更好
var array = []; 
for(var i = 0; i < 10000; i++){
    array[i] = "<li>"+i+"</li>"; 
}
$myList.html(array.join(''));

性能提高有一点需要记住的,就是访问DOM接口的时候。为什么要缓存得到的变量?就是减少访问DOM接口,可以将DOM访问看作数据库访问,每访问一次就需要消耗一些资源。

3.不要处理不存在的元素。详情

// 无良的做法:jQuery后台要跑完三个函数后才会知道这个元素其实根本不存在
$("#nosuchthing").slideUp();
// 应该这样
var $mySelection = $("#nosuchthing");
if ($mySelection.length) {
    $mySelection.slideUp();
}

事件相关-Events

1.一个页面只写一个文档 ready 事件的处理程序。这样代码既清晰好调试,又容易跟踪代码的过程。

2.不要用匿名函数来绑定事件。匿名函数不易调试维护测试和复用。看看这里吧

$("#myLink").on("click", function(){...}); // BAD,雅蠛蝶不要这样
 
// GOOD
function myLinkClickHandler(){...}
$("#myLink").on("click", myLinkClickHandler);

3.处理文档ready事件也不要用匿名函数,在说一次,匿名函数不利于调试、维护、测试和复用 :(

$(function(){ ... }); // 糟糕的做法:无法复用此函数也无法为其写测试用例
 
// 好的做法
$(initPage); // 或者 $(document).ready(initPage);
function initPage(){
    // 写页面加载事件的地方
}

4.进一步,最好也将ready事件的处理程序放到外部文件中引入到页面,而页面中内嵌的代码只需调用即可,(复用性大大提高)。

<script src="my-document-ready.js"></script>
<script>
	// 初始化一些必要的全局变量
	$(document).ready(initPage); // 或者 $(initPage);
</script>

5.千万不要写内联函数到HTML的JS代码,这是调试的梦魇!应该总是用jQuery来绑定事件自带程序,这样也方便随时动态地绑定(复用性)和取消绑定(操作简单),还有一点是有利于调试和测试。

<a id="myLink" href="#" οnclick="myEventHandler();">my link</a> <!--不好 -->

$("#myLink").on("click", myEventHandler); // GOOD

6.如果可能使用自己的一个事件命名空间(custom namespace),这样可以方便地取消绑定而不会影响其他DOM元素的事件绑定。

$("#myLink").on("click.mySpecialClick", myEventHandler); // 不错
// 之后,让我们优雅地解除绑定
$("#myLink").unbind("click.mySpecialClick");

7.使用事件委托当你要对多个元素绑定相同的时间的时候。事件委托允许我们用一个事件监听器(事件处理函数),绑定在父元素(也可以是祖先元素)上。这样可以减少页面元素绑定的事件处理函数的数量,减少内存消耗和绑定的时间消耗。

$("#list a").on("click", myClickHandler); // BAD,你会给所有的a标签绑定事件
// 好的做法,只有一个事件被绑定到祖先元素,然后再函数里面再进行判别
$("#list a").on("click", "a", myClickHandler);

异步操作-Ajax

1.直接用$.ajax()而不要去用 .getJson()或 .get(),因为jQuery内部还是将其转为前者

2.不要对HTTPS站点使用HTTP发起请求,最好干脆就不要指定(将HTTP或者HTTPS从你的URL中移除)

3.不要在链接里面嵌参数,请使用数据对象(data Object)来传递设置

// 不易阅读的代码...
$.ajax({
    url: "something.php?param1=test1¶m2=test2",
    ....
});
 
// 更易阅读...
$.ajax({
    url: "something.php",
    data: { param1: test1, param2: test2 }
});

4.尽量指明数据类型以便你自己清楚要处理什么样的数据(见下面的Ajax标准模板)

5.对于异步动态加载(Ajax)的内容,最好使用事件委托来绑定事件处理。这样的好处是对于之后动态加载的元素事件同样有效。你或许想了解更多

委托的优点:对于后面添加到页面的元素事件委托可以检测得到。因为对祖先元素添加的一个事件,那么除非在执行事件处理否则不会去关注页面是否有什么。所以对动态新添加的元素就能重新检测得到。

$("#parent-container").on("click", "a", delegatedClickHandlerForAjax);

6.使用$.when()和.then()(Promise延迟方法),Promise延迟是属于jQuery异步模块里面的。更多例子

$.ajax({ ... }).then(successHandler, failureHandler);
 
// 或者
var jqxhr = $.ajax({ ... });
jqxhr.done(successHandler);
jqxhr.fail(failureHandler);

7.标准的Ajax模板。追寻根源

var jqxhr = $.ajax({
    url: url,
    type: "GET", // 默认为GET,你可以根据需要更改
    cache: true, // 默认为true,但对于script,jsonp类型为false,可以自行设置
    data: {}, // 将请求参数放这里.
    dataType: "json", // 指定想要的数据类型
    jsonp: "callback", // 指定回调处理JSONP类型的请求
    statusCode: { // 如果你想处理各状态的错误的话
        404: handler404,
        500: handler500
    }
});
jqxhr.done(successHandler);
jqxhr.fail(failureHandler);

动画与特效-Effects and Animations

1.保持一个统一的风格和相同的动画实现(这里是一些设计方面的东西,如果页面动画多且杂会显得没有整齐的美感,做过设计的童鞋应该能懂作者想要表达什么)。

2.紧遵用户体验,不要滥用动画特效(动画特效应该是由用户体验驱动的)

  • 使用简洁的显示隐藏(show/hide),状态切换(toggle),滑入滑出(sideUp/slideDown)等效果来展示元素
  • 使用预设值来设置动画的速度'fast','slow',或者400(中等速度)

插件相关-Plugins

1.始终选择一个有良好支持,完善文档,全面测试过并且社区活跃的插件(怎么感觉是在说jQuery自己-_-!!)

2.注意所用插件与当前使用的jQuery版本是否兼容

3.任何常用或者可以复用的功能都可以写成jQuery插件。jQuery插件的编写模板

链式句法-Chaining

1.除了用变量将jQuery选择器返回的结果缓存,也可以用链式的写法缓存获取到的jQuery对象再调用其它方法。(jQuery比较有特色的特点,返回的都是一个jQuery封装成的数组对象,所以不管是一个还是多个元素都可以调用同一个方法)。当然你也可以用.end()或者.addSelf()或者.addBack()(addSelf和addBack是一个函数,都是指向addBack)来对jQuery的链式操作进行回滚(jQuery维护了一个链式的栈)。

$("#myDiv").addClass("error").show();

2.当链式调用多达3次以上或代码因绑定回调略显复杂时,使用换行和适当的缩进来提高代码的可读性。

$("#myLink")
    .addClass("bold")
    .on("click", myClickHandler)
    .on("mouseover", myMouseOverHandler)
    .show();

3.对于特别长的调用最好还是用变量保存下中间结果来简化代码。

其他容易混杂的地方-Miscellaneous

1.使用对象字面量来传递参数

$myLink.attr("href", "#").attr("title", "my link").attr("rel", "external"); // 糟糕:调用了三次attr
// 不错,只调用了一次attr
$myLink.attr({
    href: "#",
    title: "my link",
    rel: "external"
});

2.不要将CSS与jQuery杂揉

$("#mydiv").css({'color':red, 'font-weight':'bold'}); // BAD

.error { /* GOOD */
    color: red;
    font-weight: bold;
}

$("#mydiv").addClass("error"); /* Good */

3.不要使用摒弃了的方法,对一些公布的废弃的方法项目里面最好避免使用。时刻关注官方Changelog。点此查看所有废弃的方法

4.当需要的时候适时地结合使用原生JavaScript。jQuery的$('#')与document.getElementById原生方法的性能比较

$("#myId"); // 多少还是会逊色于原生的方法,所以在一些注重性能的地方还是要写原生的JavaScript代码
document.getElementById("myId");