最佳实践

导读

平稳退化:确保网页在不支持JavaScript的情况下也能正常工作。 分离JavaScript:把网页的结构和内容与JavaScript脚本的动作分开。 向后兼容性:确保老版本的浏览器不会因为你的JavaScript脚本而死掉。 性能考虑:确保脚本执行的性能最优。

平稳退化

如果正确地使用了JavaScript脚本,就可以让访问者在他们的浏览器不支持JavaScript的情况下仍能顺利地浏览你的网站。这就是所谓的平稳退化(graceful degradation),就是说,虽然某些功能无法使用,但最基本的操作仍能顺利完成。

分离JavaScript

例如,如何用下面给这条语句来表明“当这个链接被点击时,它将调用popUp()函数”的意思呢?

<a href="http://www.example.com/" class="popUp">Example</a>

获取这个事件的元素:

var links=document.getElementsByTagName("a");
for (var i=0;i<links.length;i++){
		if(links[i].getAttribute("class")=="popUp"){
				links[i].onclick=function(){
						popUp(this.getAttribute("href"));
						return false;
					}
			}
	}

以上代码将把调用popUp()函数的onclick事件添加到有关的链接上。只要把它们存入一个外部JavaScript文件,就等于是把这些操作从HTML文档里分离出来了。而这就是“分离JavaScript”的含义。 还有个问题需要解决:如果把这段代码存入外部JavaScript文件,它们将无法正常运行。因为这段代码的第一行是:

var links=document.getElementsByTagName("a");

这条语句将在JavaScript文件被加载时立刻执行。如果JavaScript文件是从HTML文档<head>部分用<script>标签调用的,它将在HTML文档之前加载到浏览器里。同样,如果<script>标签位于文档底部</body>之前,就不能保证哪个文件最先结束加载(浏览器可能一次加载多个)。因为脚本加载时文档可能不完整,所以模型也不完整。没有完整的DOM,getElementsByTagName等方法就不能正常工作。必须让这些代码在HTML文档全部加载到浏览器之后马上开始执行。 文档将被加载到一个浏览器窗口里,document对象又是window对象的一个属性。当window对象触发onload事件时,document对象已经存在。 将上述代码打包在preparelLinks函数里,并把这个函数添加到window对象的onload事件上去。这样一来,DOM就可以正常工作了:

window.onload=prepareLinks;
function prepareLinks(){
	var links=document.getElementsByTagName("a");
        for (var i=0;i<links.length;i++){
		if(links[i].getAttribute("class")=="popUp"){
				links[i].onclick=function(){
						popUp(link[i].getAttribute("href"));
						return false;
					}
			}
	}
function popUp(winURL){
		window.open(winURL,"popUp","width=320,height=480");  //三个参数可选:网页URL、新窗口名字、新窗口属性
	}

}

向后兼容

由于某些原因,你的网站访问者可能未启用JavaScript功能。此外,不同浏览器对JavaScript的支持程度也不一样。绝大多数现代的浏览器对DOM的支持都非常不错,但比较古老的浏览器却可能无法理解DOM提供的方法和属性。因此,即使某位用户在访问你的网站时使用的是支持JavaScript的浏览器,某些脚本也不一定能正常工作。

对象检测

针对这一问题的最简单解决办法就是:检测浏览器对JavaScript的支持程度。这种检测称为对象检测

if (method){
	statements
	}

例如,如果有一个使用了getElementById()方法的函数,就可以在调用getElementById()方法之前先检查用户所使用的浏览器是否支持这个方法。在使用对象检测时,一定要删掉方法名后面的圆括号,如果不删掉,测试的将是方法的结果,无论方法是否存在。

function myFunction(){
	if(document.getElementsById){
			statements using getElementById
		}
	}

因此,如果某个浏览器不支持getElementById()方法,它就永远也不会执行使用此方法的语句。 这个解决方案的唯一不足是,如此编写出来的函数会增加一对花括号。如果需要在函数里检测多个DOM方法和/或属性是否存在,这个函数中最重要的语句就会被深埋在一层又一层的花括号里。而这样的代码往往很难阅读和理解。 我们可以换种角度:

if(!getElementById) return false;

例如测试浏览器是否支持getElementById和getElementsByTagName:

if(!getElementById || !getElementsByTagName) return false;

虽然只是一条简单的if语句,但它可以确保那些“古老的”的浏览器不会因为我的脚本代码而出问题。这么做是为了让脚本有良好的向后兼容性。因为在给网页添加各有关行为时始终遵循了“渐进增强”的原则,所以可以确切地知道我添加的那些功能都能平稳退化,我的网页在那些“古老的”浏览器里也能正常浏览。那些只支持一部分JavaScript功能但不支持DOM的浏览器仍可以访问我的网页的内容。

浏览器嗅探技术

“浏览器嗅探”指通过提取浏览器供应商提供的信息来解决向后兼容性问题。

性能考虑

尽量少访问DOM和尽量减少标记

访问DOM的方式对脚本性能会产生非常大的影响。以下面代码为例:

if(document.getElementsByTagName("a").length>0){
		var links=document.getElementsByTagName("a");
		for(var i=0;i<links.length;i++){
				//对每个链接做点处理
			}
	}

虽然这段代码可以运行,但它不能保持最优的性能。**不管什么时候,只要是查询DOM中的某些元素,浏览器都会搜索整个DOM树,从中查找可能匹配的元素。**这段代码使用了两次getElementsByTagName方法去执行相同的操作,浪费了一次搜索。更好的办法是把第一次搜索的结果保存在一个变量中,然后在循环里重用该结果:

var links=document.getElementsByTagName("a");
if(links.length>0){
		for(var i=0;i<links.length;i++){
				//对每个链接做点处理
			}
	}

这样一来,代码功能没有变,但搜索DOM的次数由两次降低到了一次。 在多个函数都会取得一组类似元素的情况下,可以考虑重构代码,把搜索结果保存在一个全局变量里,或者把一组元素直接以参数形式传递给函数。 另一个需要注意的地方,就是要尽量减少文档中的标记数量。过多不必要的元素只会增加DOM树的规模,进而增加遍历DOM树以查找特定元素的时间。

合并和放置脚本

包含脚本的最佳方式就是使用外部文件。不过下面这种方式最好不要出现:

<script src="script/functionA.js"></script>
<script src="script/functionB.js"></script>
<script src="script/functionC.js"></script>
<script src="script/functionD.js"></script>

推荐做法是把functionA.js、functionB.js、functionC.js、functionD.js合并到一个脚本文件中。这样,就可以减少加载页面时发送的请求数量。而减少请求数量通常都是在性能优化时首先要考虑的。 脚本在标记中的位置对页面的初次加载时间也有很大影响。传统上,我们都把脚本放在文档的<head>区域,这种放置问题有一个问题。位于<head>块中的脚本会导致浏览器无法并行加载其他文件(如图像或其他脚本)。一般来说,根据HTTP规范,浏览器每次从同一个域名中最多只能同时下载两个文件。而在下载脚本期间,浏览器不会下载其他任何文件,即使是来自不同域名的文件也不会下载,所有其他资源都要等脚本加载完毕后才能下载。 把所有的<script>标签都放到文档的末尾,</body>标记之前,就可以让页面变得更快。即使这样,在加载脚本时,window对象的load事件依然可以执行对文档进行的各种操作。

压缩脚本

所谓压缩脚本,指的是把脚本文件中不必要的字节,如空格和注释,统统删除,从而达到“压缩”文件的目的。 例如以下代码:

function showPic(whichpic){
	var source = whichpic.getAttribute("href");
	var placeholder = document.getElementById("placeholder");
	placeholder.setAttribute("src",source);
	
	var text=whichpic.getAttribute("title");
	var description=document.getElementById("description");
	description.firstChild.nodeValue=text;
	}

压缩之后的代码就会变成下面这样:

function showPic(whichpic){var source = whichpic.getAttribute("href");var placeholder = document.getElementById("placeholder");placeholder.setAttribute("src",source);var text=whichpic.getAttribute("title");var description=document.getElementById("description");description.firstChild.nodeValue=text;}

精简后的代码虽然不容易看懂,却能大幅减少文件大小。多数情况下,你应该有两个版本,一个是工作副本,可以修改代码并添加注释;另一个是精简副本,用于放在站点上。通常,为了与非精简版本区分开,最好在精简副本的文件名中加上min字样:

<script src="script/scriptName.min.js"></script>

推荐的代码压缩工具:

Last updated